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();