*** linuxthreads-old/mutex.c Wed Nov 18 18:59:53 1998 --- linuxthreads/mutex.c Mon Jun 12 12:35:43 2000 *************** *** 37,43 **** int __pthread_mutex_destroy(pthread_mutex_t * mutex) { ! if (mutex->__m_lock.__status != 0) return EBUSY; return 0; } strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) --- 37,43 ---- int __pthread_mutex_destroy(pthread_mutex_t * mutex) { ! if ((mutex->__m_lock.__status & 1) != 0) return EBUSY; return 0; } strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) *************** *** 120,126 **** __pthread_unlock(&mutex->__m_lock); return 0; case PTHREAD_MUTEX_ERRORCHECK_NP: ! if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0) return EPERM; mutex->__m_owner = NULL; __pthread_unlock(&mutex->__m_lock); --- 120,126 ---- __pthread_unlock(&mutex->__m_lock); return 0; case PTHREAD_MUTEX_ERRORCHECK_NP: ! if (mutex->__m_owner != thread_self() || (mutex->__m_lock.__status & 1) == 0) return EPERM; mutex->__m_owner = NULL; __pthread_unlock(&mutex->__m_lock); diff -c linuxthreads-old/spinlock.c linuxthreads/spinlock.c *** linuxthreads-old/spinlock.c Wed Jan 5 11:07:35 2000 --- linuxthreads/spinlock.c Mon Jun 12 16:17:23 2000 *************** *** 22,34 **** #include "spinlock.h" #include "restart.h" ! /* The status field of a fastlock has the following meaning: ! 0: fastlock is free ! 1: fastlock is taken, no thread is waiting on it ! ADDR: fastlock is taken, ADDR is address of thread descriptor for ! first waiting thread, other waiting threads are linked via ! their p_nextlock field. ! 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). For __pthread_unlock, we perform a linear search in the list --- 22,42 ---- #include "spinlock.h" #include "restart.h" ! /* 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). For __pthread_unlock, we perform a linear search in the list *************** *** 40,59 **** pthread_descr self) { long oldstatus, newstatus; ! int spurious_wakeup_count = 0; do { oldstatus = lock->__status; ! if (oldstatus == 0) { ! newstatus = 1; } else { if (self == NULL) self = thread_self(); ! newstatus = (long) self; } if (self != NULL) { ASSERT(self->p_nextlock == NULL); ! THREAD_SETMEM(self, p_nextlock, (pthread_descr) oldstatus); } } while(! compare_and_swap(&lock->__status, oldstatus, newstatus, &lock->__spinlock)); --- 48,73 ---- pthread_descr self) { long oldstatus, newstatus; ! int successful_seizure, spurious_wakeup_count = 0; ! ! again: do { oldstatus = lock->__status; ! successful_seizure = 0; ! ! if ((oldstatus & 1) == 0) { ! newstatus = oldstatus | 1; ! successful_seizure = 1; } else { if (self == NULL) self = thread_self(); ! newstatus = (long) self | 1; } + if (self != NULL) { ASSERT(self->p_nextlock == NULL); ! THREAD_SETMEM(self, p_nextlock, (pthread_descr) (oldstatus & ~1L)); } } while(! compare_and_swap(&lock->__status, oldstatus, newstatus, &lock->__spinlock)); *************** *** 64,70 **** 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) { for (;;) { suspend(self); if (self->p_nextlock != NULL) { --- 78,84 ---- 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 (!successful_seizure) { for (;;) { suspend(self); if (self->p_nextlock != NULL) { *************** *** 74,79 **** --- 88,94 ---- } break; } + goto again; } /* Put back any resumes we caught that don't belong to us. */ *************** *** 87,93 **** --- 102,112 ---- 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 *************** *** 99,108 **** } /* Find thread in waiting queue with maximal priority */ ptr = (pthread_descr *) &lock->__status; ! thr = (pthread_descr) oldstatus; maxprio = 0; maxptr = ptr; ! while (thr != (pthread_descr) 1) { if (thr->p_priority >= maxprio) { maxptr = ptr; maxprio = thr->p_priority; --- 118,127 ---- } /* Find thread in waiting queue with maximal priority */ ptr = (pthread_descr *) &lock->__status; ! thr = (pthread_descr) (oldstatus & ~1L); maxprio = 0; maxptr = ptr; ! while (thr != 0) { if (thr->p_priority >= maxprio) { maxptr = ptr; maxprio = thr->p_priority; *************** *** 110,129 **** ptr = &(thr->p_nextlock); thr = *ptr; } /* 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, oldstatus, (long)(thr->p_nextlock), &lock->__spinlock)) goto again; } else { /* No risk of concurrent access, remove max prio thread normally */ thr = *maxptr; *maxptr = thr->p_nextlock; } /* Wake up the selected waiting thread */ thr->p_nextlock = NULL; restart(thr); --- 129,162 ---- ptr = &(thr->p_nextlock); thr = *ptr; } + /* 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. 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)); } + /* Wake up the selected waiting thread */ thr->p_nextlock = NULL; restart(thr);