Index: Python/thread_pthread.h
===================================================================
--- Python/thread_pthread.h (revision 79534)
+++ Python/thread_pthread.h (working copy)
@@ -90,19 +90,28 @@
* -> a thread tries to unlock a mutex locked by a different thread
* pthread mutexes are designed for serializing threads over short pieces
* of code anyway, so wouldn't be an appropriate implementation of
- * Python's locks regardless.
+ * Python's locks regardless, because they don't necessarily provide
+ * fairness to waiting threads.
*
- * The pthread_lock struct implements a Python lock as a "locked?" bit
- * and a pair. In general, if the bit can be acquired
- * instantly, it is, else the pair is used to block the thread until the
- * bit is cleared. 9 May 1994 [email protected]
+ * The pthread_lock struct implements a Python lock with a state variable
+ * and a pair. In addition, a count of "waiting" hreads
+ * is maintained. When the lock is released, it is handed off waiting thread
+ * if one is present. This ensures that all competing threads have to queue
+ * on the condition variable, thus ensuring that they all get their fair turn.
+ * In particular, a thread cannot instantly claim a just-released lock if
+ * others are waiting for it.
+ * state = 0 is unlockded.
+ * state = 1 is locked
+ * state = 2 is handoff state, meaning that it has been handed off to a thread
+ * that hasn't yet woken up to claim the lock.
*/
typedef struct {
- char locked; /* 0=unlocked, 1=locked */
/* a pair to handle an acquire of a locked lock */
pthread_cond_t lock_released;
pthread_mutex_t mut;
+ int n_waiting; /* number of threads waiting for handoff */
+ char state; /* 0=unlocked, 1=locked, 2=handoff */
} pthread_lock;
#define CHECK_STATUS(name) if (status != 0) { perror(name); error = 1; }
@@ -353,7 +362,8 @@
lock = (pthread_lock *) malloc(sizeof(pthread_lock));
if (lock) {
memset((void *)lock, '\0', sizeof(pthread_lock));
- lock->locked = 0;
+ lock->n_waiting = 0;
+ lock->state = 0;
status = pthread_mutex_init(&lock->mut,
pthread_mutexattr_default);
@@ -401,25 +411,23 @@
status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS("pthread_mutex_lock[1]");
- success = thelock->locked == 0;
+ success = thelock->state == 0;
if ( !success && waitflag ) {
- /* continue trying until we get the lock */
-
- /* mut must be locked by me -- part of the condition
- * protocol */
- while ( thelock->locked ) {
+ /* wait at least once until lock handoff */
+ thelock->n_waiting++;
+ do {
status = pthread_cond_wait(&thelock->lock_released,
&thelock->mut);
CHECK_STATUS("pthread_cond_wait");
- }
+ } while ( thelock->state != 2 );
success = 1;
}
- if (success) thelock->locked = 1;
+ if (success) thelock->state = 1; /* it is now locked */
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS("pthread_mutex_unlock[1]");
- if (error) success = 0;
+ if (error) success = 0; /* really should be a fatal error */
dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
return success;
}
@@ -434,15 +442,18 @@
status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS("pthread_mutex_lock[3]");
+ assert( thelock->state == 1 ); /* locked */
- thelock->locked = 0;
+ if ( thelock->n_waiting > 0 ) {
+ thelock->state = 2; /* lock handoff */
+ thelock->n_waiting--;
+ status = pthread_cond_signal( &thelock->lock_released );
+ CHECK_STATUS("pthread_cond_signal");
+ } else
+ thelock->state = 0; /* lock free */
status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS("pthread_mutex_unlock[3]");
-
- /* wake up someone (anyone, if any) waiting on the lock */
- status = pthread_cond_signal( &thelock->lock_released );
- CHECK_STATUS("pthread_cond_signal");
}
#endif /* USE_SEMAPHORES */