[PATCH] [2.4] Waitable counter structure

From: Luca Barbieri (ldb@ldb.ods.org)
Date: Thu May 30 2002 - 13:11:53 EST


"Waitable counter": object that keeps a count and allows to wait until
it reaches zero or is no longer zero.

Sample usage: keep track of requests and, on driver shutdown, wait for
completion of all the requests (counter goes to 0) before completing the
shutdown (an alternative design is to have the last request completed
perform the shutdown, but this isn't always possible).

Actually having to do something like the sample seems so common that it
seems strange that there isn't already something to handle this. Am I an
obvious simpler way of doing this? (other than scheduling in a loop,
which is unelegant and inappropriate if the task might wait for a long
time)

diff --exclude-from=/home/ldb/src/linux-exclude -u -r -N linux-base/include/linux/waitcount.h linux/include/linux/waitcount.h
--- linux-base/include/linux/waitcount.h Thu Jan 1 01:00:00 1970
+++ linux/include/linux/waitcount.h Thu May 30 16:28:45 2002
@@ -0,0 +1,67 @@
+#ifndef __LINUX_WAITCOUNT_H
+#define __LINUX_WAITCOUNT_H
+
+#include <linux/config.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+/* "Waitable counter": object that keeps a count and allows to wait
+ until it reaches zero or is no longer zero.
+ */
+
+struct waitable_count {
+ atomic_t count;
+ wait_queue_head_t waitq;
+};
+
+
+#define __WAITABLE_COUNT_INITIALIZER(name) (struct waitable_count) {ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).waitq)}
+#define INITIALIZE_WAITABLE_COUNT(name) (name) = __WAITABLE_COUNT_INITIALIZER(name)
+#define DECLARE_WAITABLE_COUNT(name) struct waitable_count name = __WAITABLE_COUNT_INITIALIZER(name)
+
+static inline void
+inc_count(struct waitable_count *wcount)
+{
+ atomic_inc(&wcount->count);
+}
+
+static inline void
+dec_count_wake_up(struct waitable_count *wcount)
+{
+ if (atomic_dec_and_test(&wcount->count) && waitqueue_active(&wcount->waitq)) {
+ wake_up_all(&wcount->waitq);
+ }
+}
+
+static inline void
+inc_count_wake_up(struct waitable_count *wcount)
+{
+ if (!atomic_inc_and_test(&wcount->count) && waitqueue_active(&wcount->waitq)) {
+ wake_up_all(&wcount->waitq);
+ }
+}
+
+static inline void
+dec_count(struct waitable_count *wcount)
+{
+ atomic_dec(&wcount->count);
+}
+
+#define inc_nonzero inc_count_wake_up
+#define dec_nonzero dec_count
+#define inc_zero inc_count
+#define dec_zero dec_count_wake_up
+
+void
+__wait_count(struct waitable_count *wcount, int nonzero, int state);
+
+static inline void
+wait_count(struct waitable_count *wcount, int nonzero, int state)
+{
+ if (!atomic_read(&wcount->count) == nonzero)
+ {
+ __wait_count (wcount, nonzero, state);
+ }
+}
+
+#endif
diff --exclude-from=/home/ldb/src/linux-exclude -u -r -N linux-base/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-base/kernel/ksyms.c Wed Apr 10 14:37:33 2002
+++ linux/kernel/ksyms.c Thu May 30 16:29:13 2002
@@ -47,6 +47,7 @@
 #include <linux/in6.h>
 #include <linux/completion.h>
 #include <linux/seq_file.h>
+#include <linux/waitcount.h>
 #include <asm/checksum.h>
 
 #if defined(CONFIG_PROC_FS)
@@ -472,6 +473,7 @@
 EXPORT_SYMBOL(sleep_on_timeout);
 EXPORT_SYMBOL(interruptible_sleep_on);
 EXPORT_SYMBOL(interruptible_sleep_on_timeout);
+EXPORT_SYMBOL(__wait_count);
 EXPORT_SYMBOL(schedule);
 EXPORT_SYMBOL(schedule_timeout);
 EXPORT_SYMBOL(jiffies);
diff --exclude-from=/home/ldb/src/linux-exclude -u -r -N linux-base/kernel/sched.c linux/kernel/sched.c
--- linux-base/kernel/sched.c Wed Apr 10 14:37:29 2002
+++ linux/kernel/sched.c Thu May 30 16:29:28 2002
@@ -27,6 +27,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
 #include <linux/completion.h>
+#include <linux/waitcount.h>
 #include <linux/prefetch.h>
 #include <linux/compiler.h>
 
@@ -851,6 +852,22 @@
         SLEEP_ON_TAIL
 
         return timeout;
+}
+
+void
+__wait_count(struct waitable_count *wcount, int nonzero, int state)
+{
+#define q (&wcount->waitq)
+ SLEEP_ON_VAR
+
+ current->state = state;
+
+ SLEEP_ON_HEAD
+ if (!atomic_read(&wcount->count) == nonzero)
+ schedule();
+ current->state = TASK_RUNNING;
+ SLEEP_ON_TAIL
+#undef q
 }
 
 void scheduling_functions_end_here(void) { }



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri May 31 2002 - 22:00:29 EST