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 tim@ksr.com + * 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 */