[parisc-linux-cvs] Semaphores again
Matthew Wilcox
willy@ldl.fc.hp.com
Sun, 18 Mar 2001 13:48:10 -0700
* Update documentation to reflect the code.
* Correct operation with multiple processes allowed to take a semaphore.
Index: arch/parisc/kernel/semaphore.c
===================================================================
RCS file: /home/cvs/parisc/linux/arch/parisc/kernel/semaphore.c,v
retrieving revision 1.7
diff -u -p -r1.7 semaphore.c
--- semaphore.c 2001/03/17 04:29:24 1.7
+++ semaphore.c 2001/03/18 20:38:36
@@ -1,36 +1,26 @@
/*
- * Semaphore implementation Copyright (c) 2001 Matthew Wilcox
+ * Semaphore implementation Copyright (c) 2001 Matthew Wilcox, Hewlett-Packard
*/
#include <linux/sched.h>
#include <linux/spinlock.h>
/*
- * Semaphores are implemented using a two-way counter:
- * The "count" variable is decremented for each process
- * that tries to sleep, while the "waking" variable is
- * incremented when the "up()" code goes to wake up waiting
- * processes.
+ * Semaphores are complex as we wish to avoid using two variables.
+ * `count' has multiple roles, depending on its value. If it is positive
+ * or zero, there are no waiters. The functions here will never be
+ * called; see <asm/semaphore.h>
*
- * Notably, the inline "up()" and "down()" functions can
- * efficiently test if they need to do any extra work (up
- * needs to do something only if count was negative before
- * the increment operation.
+ * When count is -1 it indicates there is at least one task waiting
+ * for the semaphore.
*
- * When __up() is called, the count was negative before
- * incrementing it, and we need to wake up somebody.
+ * When count is less than that, there are '- count - 1' wakeups
+ * pending. ie if it has value -3, there are 2 wakeups pending.
*
- * This routine adds one to the count of processes that need to
- * wake up and exit. ALL waiting processes actually wake up but
- * only the one that gets to the "waking" field first will gate
- * through and acquire the semaphore. The others will go back
- * to sleep.
- *
- * Note that these functions are only called when there is
- * contention on the lock, and as such all this is the
- * "non-critical" part of the whole semaphore business. The
- * critical part is the inline stuff in <asm/semaphore.h>
- * where we want to avoid any extra jumps and calls.
+ * Note that these functions are only called when there is contention
+ * on the lock, and as such all this is the "non-critical" part of the
+ * whole semaphore business. The critical part is the inline stuff in
+ * <asm/semaphore.h> where we want to avoid any extra jumps and calls.
*/
void __up(struct semaphore *sem)
{
@@ -38,6 +28,8 @@ void __up(struct semaphore *sem)
wake_up(&sem->wait);
}
+#define wakers(count) (-1 - count)
+
#define DOWN_HEAD \
DECLARE_WAITQUEUE(wait, current); \
\
@@ -53,16 +45,17 @@ void __up(struct semaphore *sem)
#define DOWN_TAIL \
spin_lock_irq(&sem->sentry); \
- if (sem->count == -1) \
+ if (wakers(sem->count) == 0) \
goto lost_race; /* Someone stole our wakeup */ \
__remove_wait_queue(&sem->wait, &wait); \
current->state = TASK_RUNNING; \
- if (!waitqueue_active(&sem->wait)) { \
- sem->count = 0; \
- } else { \
- sem->count += 1; \
- }
+ if (!waitqueue_active(&sem->wait) && (sem->count < 0)) \
+ sem->count = wakers(sem->count);
+#define UPDATE_COUNT \
+ sem->count += (sem->count < 0) ? 1 : - 1;
+
+
void __down(struct semaphore * sem)
{
DOWN_HEAD
@@ -76,6 +69,7 @@ void __down(struct semaphore * sem)
}
DOWN_TAIL
+ UPDATE_COUNT
}
int __down_interruptible(struct semaphore * sem)
@@ -98,6 +92,10 @@ int __down_interruptible(struct semaphor
}
DOWN_TAIL
+
+ if (!ret) {
+ UPDATE_COUNT
+ }
return ret;
}
Index: include/asm-parisc/semaphore.h
===================================================================
RCS file: /home/cvs/parisc/linux/include/asm-parisc/semaphore.h,v
retrieving revision 1.7
diff -u -p -r1.7 semaphore.h
--- semaphore.h 2001/03/17 04:29:25 1.7
+++ semaphore.h 2001/03/18 20:38:36
@@ -79,9 +79,11 @@ extern __inline__ void down(struct semap
#endif
spin_lock_irq(&sem->sentry);
- if (sem->count <= 0)
+ if (sem->count > 0) {
+ sem->count--;
+ } else {
__down(sem);
- sem->count--;
+ }
spin_unlock_irq(&sem->sentry);
}
@@ -93,10 +95,11 @@ extern __inline__ int down_interruptible
#endif
spin_lock_irq(&sem->sentry);
- if (sem->count <= 0)
- ret = __down_interruptible(sem);
- if (ret == 0)
+ if (sem->count > 0) {
sem->count--;
+ } else {
+ ret = __down_interruptible(sem);
+ }
spin_unlock_irq(&sem->sentry);
return ret;
}