diff -urNp linuxthreads-0/mutex.c linuxthreads/mutex.c --- linuxthreads-0/mutex.c Wed May 24 22:26:38 2000 +++ linuxthreads/mutex.c Sun Jun 11 12:45:35 2000 @@ -38,7 +38,7 @@ strong_alias (__pthread_mutex_init, pthr int __pthread_mutex_destroy(pthread_mutex_t * mutex) { - if (mutex->__m_lock.__status != 0) return EBUSY; + if ((mutex->__m_lock.__status & 1) != 0) return EBUSY; return 0; } strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) @@ -121,7 +121,7 @@ int __pthread_mutex_unlock(pthread_mutex __pthread_unlock(&mutex->__m_lock); return 0; case PTHREAD_MUTEX_ERRORCHECK_NP: - if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0) + if (mutex->__m_owner != thread_self() || (mutex->__m_lock.__status & 1) == 0) return EPERM; mutex->__m_owner = NULL; __pthread_unlock(&mutex->__m_lock); diff -urNp linuxthreads-0/spinlock.c linuxthreads/spinlock.c --- linuxthreads-0/spinlock.c Sun May 28 15:53:04 2000 +++ linuxthreads/spinlock.c Sun Jun 11 13:16:41 2000 @@ -22,12 +22,20 @@ #include "spinlock.h" #include "restart.h" -/* The status field of a spinlock has the following meaning: - 0: spinlock is free - 1: spinlock is taken, no thread is waiting on it - ADDR: psinlock is taken, ADDR is address of thread descriptor for - first waiting thread, other waiting threads are linked via - their p_nextlock field. +/* The status field of a spinlock is a pointer whose least significant + bit is a locked flag. + + Thus the field values have the following meanings: + + status == 0: spinlock is free + status == 1: spinlock is taken; no thread is waiting on it + + (status & 1) == 1: spinlock is taken and (status & ~1L) is a + pointer to the first waiting thread; other + waiting threads are linked via the p_nextlock + field. + (status & 1) == 0: same as above, but spinlock is not taken. + The waiting list is not sorted by priority order. Actually, we always insert at top of list (sole insertion mode that can be performed without locking). @@ -40,19 +48,25 @@ void internal_function __pthread_lock(st pthread_descr self) { long oldstatus, newstatus; - int spurious_wakeup_count = 0; + int successful_seizure, spurious_wakeup_count = 0; + +again: do { oldstatus = lock->__status; - if (oldstatus == 0) { - newstatus = 1; + successful_seizure = 0; + + if ((oldstatus & 1) == 0) { + newstatus = oldstatus | 1; + successful_seizure = 1; } else { if (self == NULL) self = thread_self(); - newstatus = (long) self; + newstatus = (long) self | 1; } + if (self != NULL) { - THREAD_SETMEM(self, p_nextlock, (pthread_descr) oldstatus); + THREAD_SETMEM(self, p_nextlock, (pthread_descr) (oldstatus & ~1L)); /* Make sure the store in p_nextlock completes before performing the compare-and-swap */ MEMORY_BARRIER(); @@ -66,7 +80,7 @@ void internal_function __pthread_lock(st locks the queue to remove itself. At that point it may still be on the queue, and may be resumed by a condition signal. */ - if (oldstatus != 0) { + if (!successful_seizure) { for (;;) { suspend(self); if (self->p_nextlock != NULL) { @@ -76,6 +90,7 @@ void internal_function __pthread_lock(st } break; } + goto again; } /* Put back any resumes we caught that don't belong to us. */ @@ -89,22 +104,22 @@ int __pthread_unlock(struct _pthread_fas pthread_descr thr, * ptr, * maxptr; int maxprio; + /* If there are no waiting threads, try to atomically + give up the lock. If that works, nothing else needs to be done. */ + again: - oldstatus = lock->__status; - if (oldstatus == 0 || oldstatus == 1) { - /* No threads are waiting for this lock. Please note that we also - enter this case if the lock is not taken at all. If this wouldn't - be done here we would crash further down. */ - if (! compare_and_swap(&lock->__status, oldstatus, 0, &lock->__spinlock)) - goto again; - return 0; + + while ((oldstatus = lock->__status) == 1) { + if (compare_and_swap(&lock->__status, oldstatus, 0, &lock->__spinlock)) + return 0; } + /* Find thread in waiting queue with maximal priority */ ptr = (pthread_descr *) &lock->__status; - thr = (pthread_descr) oldstatus; + thr = (pthread_descr) (oldstatus & ~1L); maxprio = 0; maxptr = ptr; - while (thr != (pthread_descr) 1) { + while (thr != 0) { if (thr->p_priority >= maxprio) { maxptr = ptr; maxprio = thr->p_priority; @@ -121,20 +136,34 @@ again: /* Prevent reordering of the load of lock->__status above and thr->p_nextlock below */ READ_MEMORY_BARRIER(); + /* Remove max prio thread from waiting list. */ + if (maxptr == (pthread_descr *) &lock->__status) { /* If max prio thread is at head, remove it with compare-and-swap - to guard against concurrent lock operation */ - thr = (pthread_descr) oldstatus; - if (! compare_and_swap(&lock->__status, + to guard against concurrent lock operation. This removal + also has the side effect of marking the lock as released + because the new status comes from thr->p_nextlock whose + least significant bit is clear. */ + + thr = (pthread_descr) (oldstatus & ~1L); + + if (!compare_and_swap(&lock->__status, oldstatus, (long)(thr->p_nextlock), &lock->__spinlock)) goto again; } else { /* No risk of concurrent access, remove max prio thread normally */ + /* But in this case we must also flip the least significant bit + of the status to mark the lock as released */ thr = *maxptr; *maxptr = thr->p_nextlock; + + do { + oldstatus = lock->__status; + } while (!compare_and_swap(&lock->__status, oldstatus, oldstatus & ~1L, &lock->__spinlock)); } + /* Prevent reordering of store to *maxptr above and store to thr->p_nextlock below */ WRITE_MEMORY_BARRIER();