Home | History | Annotate | Line # | Download | only in intl
lock.c revision 1.1.1.1
      1 /* Locking in multithreaded situations.
      2    Copyright (C) 2005-2006 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify it
      5    under the terms of the GNU Library General Public License as published
      6    by the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    Library General Public License for more details.
     13 
     14    You should have received a copy of the GNU Library General Public
     15    License along with this program; if not, write to the Free Software
     16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
     17    USA.  */
     18 
     19 /* Written by Bruno Haible <bruno (at) clisp.org>, 2005.
     20    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
     21    gthr-win32.h.  */
     22 
     23 #include <config.h>
     24 
     25 #include "lock.h"
     26 
     27 /* ========================================================================= */
     28 
     29 #if USE_POSIX_THREADS
     30 
     31 /* Use the POSIX threads library.  */
     32 
     33 # if PTHREAD_IN_USE_DETECTION_HARD
     34 
     35 /* The function to be executed by a dummy thread.  */
     36 static void *
     37 dummy_thread_func (void *arg)
     38 {
     39   return arg;
     40 }
     41 
     42 int
     43 glthread_in_use (void)
     44 {
     45   static int tested;
     46   static int result; /* 1: linked with -lpthread, 0: only with libc */
     47 
     48   if (!tested)
     49     {
     50       pthread_t thread;
     51 
     52       if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0)
     53 	/* Thread creation failed.  */
     54 	result = 0;
     55       else
     56 	{
     57 	  /* Thread creation works.  */
     58 	  void *retval;
     59 	  if (pthread_join (thread, &retval) != 0)
     60 	    abort ();
     61 	  result = 1;
     62 	}
     63       tested = 1;
     64     }
     65   return result;
     66 }
     67 
     68 # endif
     69 
     70 /* -------------------------- gl_lock_t datatype -------------------------- */
     71 
     72 /* ------------------------- gl_rwlock_t datatype ------------------------- */
     73 
     74 # if HAVE_PTHREAD_RWLOCK
     75 
     76 #  if !defined PTHREAD_RWLOCK_INITIALIZER
     77 
     78 void
     79 glthread_rwlock_init (gl_rwlock_t *lock)
     80 {
     81   if (pthread_rwlock_init (&lock->rwlock, NULL) != 0)
     82     abort ();
     83   lock->initialized = 1;
     84 }
     85 
     86 void
     87 glthread_rwlock_rdlock (gl_rwlock_t *lock)
     88 {
     89   if (!lock->initialized)
     90     {
     91       if (pthread_mutex_lock (&lock->guard) != 0)
     92 	abort ();
     93       if (!lock->initialized)
     94 	glthread_rwlock_init (lock);
     95       if (pthread_mutex_unlock (&lock->guard) != 0)
     96 	abort ();
     97     }
     98   if (pthread_rwlock_rdlock (&lock->rwlock) != 0)
     99     abort ();
    100 }
    101 
    102 void
    103 glthread_rwlock_wrlock (gl_rwlock_t *lock)
    104 {
    105   if (!lock->initialized)
    106     {
    107       if (pthread_mutex_lock (&lock->guard) != 0)
    108 	abort ();
    109       if (!lock->initialized)
    110 	glthread_rwlock_init (lock);
    111       if (pthread_mutex_unlock (&lock->guard) != 0)
    112 	abort ();
    113     }
    114   if (pthread_rwlock_wrlock (&lock->rwlock) != 0)
    115     abort ();
    116 }
    117 
    118 void
    119 glthread_rwlock_unlock (gl_rwlock_t *lock)
    120 {
    121   if (!lock->initialized)
    122     abort ();
    123   if (pthread_rwlock_unlock (&lock->rwlock) != 0)
    124     abort ();
    125 }
    126 
    127 void
    128 glthread_rwlock_destroy (gl_rwlock_t *lock)
    129 {
    130   if (!lock->initialized)
    131     abort ();
    132   if (pthread_rwlock_destroy (&lock->rwlock) != 0)
    133     abort ();
    134   lock->initialized = 0;
    135 }
    136 
    137 #  endif
    138 
    139 # else
    140 
    141 void
    142 glthread_rwlock_init (gl_rwlock_t *lock)
    143 {
    144   if (pthread_mutex_init (&lock->lock, NULL) != 0)
    145     abort ();
    146   if (pthread_cond_init (&lock->waiting_readers, NULL) != 0)
    147     abort ();
    148   if (pthread_cond_init (&lock->waiting_writers, NULL) != 0)
    149     abort ();
    150   lock->waiting_writers_count = 0;
    151   lock->runcount = 0;
    152 }
    153 
    154 void
    155 glthread_rwlock_rdlock (gl_rwlock_t *lock)
    156 {
    157   if (pthread_mutex_lock (&lock->lock) != 0)
    158     abort ();
    159   /* Test whether only readers are currently running, and whether the runcount
    160      field will not overflow.  */
    161   /* POSIX says: "It is implementation-defined whether the calling thread
    162      acquires the lock when a writer does not hold the lock and there are
    163      writers blocked on the lock."  Let's say, no: give the writers a higher
    164      priority.  */
    165   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
    166     {
    167       /* This thread has to wait for a while.  Enqueue it among the
    168 	 waiting_readers.  */
    169       if (pthread_cond_wait (&lock->waiting_readers, &lock->lock) != 0)
    170 	abort ();
    171     }
    172   lock->runcount++;
    173   if (pthread_mutex_unlock (&lock->lock) != 0)
    174     abort ();
    175 }
    176 
    177 void
    178 glthread_rwlock_wrlock (gl_rwlock_t *lock)
    179 {
    180   if (pthread_mutex_lock (&lock->lock) != 0)
    181     abort ();
    182   /* Test whether no readers or writers are currently running.  */
    183   while (!(lock->runcount == 0))
    184     {
    185       /* This thread has to wait for a while.  Enqueue it among the
    186 	 waiting_writers.  */
    187       lock->waiting_writers_count++;
    188       if (pthread_cond_wait (&lock->waiting_writers, &lock->lock) != 0)
    189 	abort ();
    190       lock->waiting_writers_count--;
    191     }
    192   lock->runcount--; /* runcount becomes -1 */
    193   if (pthread_mutex_unlock (&lock->lock) != 0)
    194     abort ();
    195 }
    196 
    197 void
    198 glthread_rwlock_unlock (gl_rwlock_t *lock)
    199 {
    200   if (pthread_mutex_lock (&lock->lock) != 0)
    201     abort ();
    202   if (lock->runcount < 0)
    203     {
    204       /* Drop a writer lock.  */
    205       if (!(lock->runcount == -1))
    206 	abort ();
    207       lock->runcount = 0;
    208     }
    209   else
    210     {
    211       /* Drop a reader lock.  */
    212       if (!(lock->runcount > 0))
    213 	abort ();
    214       lock->runcount--;
    215     }
    216   if (lock->runcount == 0)
    217     {
    218       /* POSIX recommends that "write locks shall take precedence over read
    219 	 locks", to avoid "writer starvation".  */
    220       if (lock->waiting_writers_count > 0)
    221 	{
    222 	  /* Wake up one of the waiting writers.  */
    223 	  if (pthread_cond_signal (&lock->waiting_writers) != 0)
    224 	    abort ();
    225 	}
    226       else
    227 	{
    228 	  /* Wake up all waiting readers.  */
    229 	  if (pthread_cond_broadcast (&lock->waiting_readers) != 0)
    230 	    abort ();
    231 	}
    232     }
    233   if (pthread_mutex_unlock (&lock->lock) != 0)
    234     abort ();
    235 }
    236 
    237 void
    238 glthread_rwlock_destroy (gl_rwlock_t *lock)
    239 {
    240   if (pthread_mutex_destroy (&lock->lock) != 0)
    241     abort ();
    242   if (pthread_cond_destroy (&lock->waiting_readers) != 0)
    243     abort ();
    244   if (pthread_cond_destroy (&lock->waiting_writers) != 0)
    245     abort ();
    246 }
    247 
    248 # endif
    249 
    250 /* --------------------- gl_recursive_lock_t datatype --------------------- */
    251 
    252 # if HAVE_PTHREAD_MUTEX_RECURSIVE
    253 
    254 #  if !(defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
    255 
    256 void
    257 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
    258 {
    259   pthread_mutexattr_t attributes;
    260 
    261   if (pthread_mutexattr_init (&attributes) != 0)
    262     abort ();
    263   if (pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE) != 0)
    264     abort ();
    265   if (pthread_mutex_init (&lock->recmutex, &attributes) != 0)
    266     abort ();
    267   if (pthread_mutexattr_destroy (&attributes) != 0)
    268     abort ();
    269   lock->initialized = 1;
    270 }
    271 
    272 void
    273 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
    274 {
    275   if (!lock->initialized)
    276     {
    277       if (pthread_mutex_lock (&lock->guard) != 0)
    278 	abort ();
    279       if (!lock->initialized)
    280 	glthread_recursive_lock_init (lock);
    281       if (pthread_mutex_unlock (&lock->guard) != 0)
    282 	abort ();
    283     }
    284   if (pthread_mutex_lock (&lock->recmutex) != 0)
    285     abort ();
    286 }
    287 
    288 void
    289 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
    290 {
    291   if (!lock->initialized)
    292     abort ();
    293   if (pthread_mutex_unlock (&lock->recmutex) != 0)
    294     abort ();
    295 }
    296 
    297 void
    298 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
    299 {
    300   if (!lock->initialized)
    301     abort ();
    302   if (pthread_mutex_destroy (&lock->recmutex) != 0)
    303     abort ();
    304   lock->initialized = 0;
    305 }
    306 
    307 #  endif
    308 
    309 # else
    310 
    311 void
    312 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
    313 {
    314   if (pthread_mutex_init (&lock->mutex, NULL) != 0)
    315     abort ();
    316   lock->owner = (pthread_t) 0;
    317   lock->depth = 0;
    318 }
    319 
    320 void
    321 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
    322 {
    323   pthread_t self = pthread_self ();
    324   if (lock->owner != self)
    325     {
    326       if (pthread_mutex_lock (&lock->mutex) != 0)
    327 	abort ();
    328       lock->owner = self;
    329     }
    330   if (++(lock->depth) == 0) /* wraparound? */
    331     abort ();
    332 }
    333 
    334 void
    335 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
    336 {
    337   if (lock->owner != pthread_self ())
    338     abort ();
    339   if (lock->depth == 0)
    340     abort ();
    341   if (--(lock->depth) == 0)
    342     {
    343       lock->owner = (pthread_t) 0;
    344       if (pthread_mutex_unlock (&lock->mutex) != 0)
    345 	abort ();
    346     }
    347 }
    348 
    349 void
    350 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
    351 {
    352   if (lock->owner != (pthread_t) 0)
    353     abort ();
    354   if (pthread_mutex_destroy (&lock->mutex) != 0)
    355     abort ();
    356 }
    357 
    358 # endif
    359 
    360 /* -------------------------- gl_once_t datatype -------------------------- */
    361 
    362 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
    363 
    364 int
    365 glthread_once_singlethreaded (pthread_once_t *once_control)
    366 {
    367   /* We don't know whether pthread_once_t is an integer type, a floating-point
    368      type, a pointer type, or a structure type.  */
    369   char *firstbyte = (char *)once_control;
    370   if (*firstbyte == *(const char *)&fresh_once)
    371     {
    372       /* First time use of once_control.  Invert the first byte.  */
    373       *firstbyte = ~ *(const char *)&fresh_once;
    374       return 1;
    375     }
    376   else
    377     return 0;
    378 }
    379 
    380 #endif
    381 
    382 /* ========================================================================= */
    383 
    384 #if USE_PTH_THREADS
    385 
    386 /* Use the GNU Pth threads library.  */
    387 
    388 /* -------------------------- gl_lock_t datatype -------------------------- */
    389 
    390 /* ------------------------- gl_rwlock_t datatype ------------------------- */
    391 
    392 /* --------------------- gl_recursive_lock_t datatype --------------------- */
    393 
    394 /* -------------------------- gl_once_t datatype -------------------------- */
    395 
    396 void
    397 glthread_once_call (void *arg)
    398 {
    399   void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
    400   void (*initfunction) (void) = *gl_once_temp_addr;
    401   initfunction ();
    402 }
    403 
    404 int
    405 glthread_once_singlethreaded (pth_once_t *once_control)
    406 {
    407   /* We know that pth_once_t is an integer type.  */
    408   if (*once_control == PTH_ONCE_INIT)
    409     {
    410       /* First time use of once_control.  Invert the marker.  */
    411       *once_control = ~ PTH_ONCE_INIT;
    412       return 1;
    413     }
    414   else
    415     return 0;
    416 }
    417 
    418 #endif
    419 
    420 /* ========================================================================= */
    421 
    422 #if USE_SOLARIS_THREADS
    423 
    424 /* Use the old Solaris threads library.  */
    425 
    426 /* -------------------------- gl_lock_t datatype -------------------------- */
    427 
    428 /* ------------------------- gl_rwlock_t datatype ------------------------- */
    429 
    430 /* --------------------- gl_recursive_lock_t datatype --------------------- */
    431 
    432 void
    433 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
    434 {
    435   if (mutex_init (&lock->mutex, USYNC_THREAD, NULL) != 0)
    436     abort ();
    437   lock->owner = (thread_t) 0;
    438   lock->depth = 0;
    439 }
    440 
    441 void
    442 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
    443 {
    444   thread_t self = thr_self ();
    445   if (lock->owner != self)
    446     {
    447       if (mutex_lock (&lock->mutex) != 0)
    448 	abort ();
    449       lock->owner = self;
    450     }
    451   if (++(lock->depth) == 0) /* wraparound? */
    452     abort ();
    453 }
    454 
    455 void
    456 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
    457 {
    458   if (lock->owner != thr_self ())
    459     abort ();
    460   if (lock->depth == 0)
    461     abort ();
    462   if (--(lock->depth) == 0)
    463     {
    464       lock->owner = (thread_t) 0;
    465       if (mutex_unlock (&lock->mutex) != 0)
    466 	abort ();
    467     }
    468 }
    469 
    470 void
    471 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
    472 {
    473   if (lock->owner != (thread_t) 0)
    474     abort ();
    475   if (mutex_destroy (&lock->mutex) != 0)
    476     abort ();
    477 }
    478 
    479 /* -------------------------- gl_once_t datatype -------------------------- */
    480 
    481 void
    482 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
    483 {
    484   if (!once_control->inited)
    485     {
    486       /* Use the mutex to guarantee that if another thread is already calling
    487 	 the initfunction, this thread waits until it's finished.  */
    488       if (mutex_lock (&once_control->mutex) != 0)
    489 	abort ();
    490       if (!once_control->inited)
    491 	{
    492 	  once_control->inited = 1;
    493 	  initfunction ();
    494 	}
    495       if (mutex_unlock (&once_control->mutex) != 0)
    496 	abort ();
    497     }
    498 }
    499 
    500 int
    501 glthread_once_singlethreaded (gl_once_t *once_control)
    502 {
    503   /* We know that gl_once_t contains an integer type.  */
    504   if (!once_control->inited)
    505     {
    506       /* First time use of once_control.  Invert the marker.  */
    507       once_control->inited = ~ 0;
    508       return 1;
    509     }
    510   else
    511     return 0;
    512 }
    513 
    514 #endif
    515 
    516 /* ========================================================================= */
    517 
    518 #if USE_WIN32_THREADS
    519 
    520 /* -------------------------- gl_lock_t datatype -------------------------- */
    521 
    522 void
    523 glthread_lock_init (gl_lock_t *lock)
    524 {
    525   InitializeCriticalSection (&lock->lock);
    526   lock->guard.done = 1;
    527 }
    528 
    529 void
    530 glthread_lock_lock (gl_lock_t *lock)
    531 {
    532   if (!lock->guard.done)
    533     {
    534       if (InterlockedIncrement (&lock->guard.started) == 0)
    535 	/* This thread is the first one to need this lock.  Initialize it.  */
    536 	glthread_lock_init (lock);
    537       else
    538 	/* Yield the CPU while waiting for another thread to finish
    539 	   initializing this lock.  */
    540 	while (!lock->guard.done)
    541 	  Sleep (0);
    542     }
    543   EnterCriticalSection (&lock->lock);
    544 }
    545 
    546 void
    547 glthread_lock_unlock (gl_lock_t *lock)
    548 {
    549   if (!lock->guard.done)
    550     abort ();
    551   LeaveCriticalSection (&lock->lock);
    552 }
    553 
    554 void
    555 glthread_lock_destroy (gl_lock_t *lock)
    556 {
    557   if (!lock->guard.done)
    558     abort ();
    559   DeleteCriticalSection (&lock->lock);
    560   lock->guard.done = 0;
    561 }
    562 
    563 /* ------------------------- gl_rwlock_t datatype ------------------------- */
    564 
    565 static inline void
    566 gl_waitqueue_init (gl_waitqueue_t *wq)
    567 {
    568   wq->array = NULL;
    569   wq->count = 0;
    570   wq->alloc = 0;
    571   wq->offset = 0;
    572 }
    573 
    574 /* Enqueues the current thread, represented by an event, in a wait queue.
    575    Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
    576 static HANDLE
    577 gl_waitqueue_add (gl_waitqueue_t *wq)
    578 {
    579   HANDLE event;
    580   unsigned int index;
    581 
    582   if (wq->count == wq->alloc)
    583     {
    584       unsigned int new_alloc = 2 * wq->alloc + 1;
    585       HANDLE *new_array =
    586 	(HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
    587       if (new_array == NULL)
    588 	/* No more memory.  */
    589 	return INVALID_HANDLE_VALUE;
    590       /* Now is a good opportunity to rotate the array so that its contents
    591 	 starts at offset 0.  */
    592       if (wq->offset > 0)
    593 	{
    594 	  unsigned int old_count = wq->count;
    595 	  unsigned int old_alloc = wq->alloc;
    596 	  unsigned int old_offset = wq->offset;
    597 	  unsigned int i;
    598 	  if (old_offset + old_count > old_alloc)
    599 	    {
    600 	      unsigned int limit = old_offset + old_count - old_alloc;
    601 	      for (i = 0; i < limit; i++)
    602 		new_array[old_alloc + i] = new_array[i];
    603 	    }
    604 	  for (i = 0; i < old_count; i++)
    605 	    new_array[i] = new_array[old_offset + i];
    606 	  wq->offset = 0;
    607 	}
    608       wq->array = new_array;
    609       wq->alloc = new_alloc;
    610     }
    611   event = CreateEvent (NULL, TRUE, FALSE, NULL);
    612   if (event == INVALID_HANDLE_VALUE)
    613     /* No way to allocate an event.  */
    614     return INVALID_HANDLE_VALUE;
    615   index = wq->offset + wq->count;
    616   if (index >= wq->alloc)
    617     index -= wq->alloc;
    618   wq->array[index] = event;
    619   wq->count++;
    620   return event;
    621 }
    622 
    623 /* Notifies the first thread from a wait queue and dequeues it.  */
    624 static inline void
    625 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
    626 {
    627   SetEvent (wq->array[wq->offset + 0]);
    628   wq->offset++;
    629   wq->count--;
    630   if (wq->count == 0 || wq->offset == wq->alloc)
    631     wq->offset = 0;
    632 }
    633 
    634 /* Notifies all threads from a wait queue and dequeues them all.  */
    635 static inline void
    636 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
    637 {
    638   unsigned int i;
    639 
    640   for (i = 0; i < wq->count; i++)
    641     {
    642       unsigned int index = wq->offset + i;
    643       if (index >= wq->alloc)
    644 	index -= wq->alloc;
    645       SetEvent (wq->array[index]);
    646     }
    647   wq->count = 0;
    648   wq->offset = 0;
    649 }
    650 
    651 void
    652 glthread_rwlock_init (gl_rwlock_t *lock)
    653 {
    654   InitializeCriticalSection (&lock->lock);
    655   gl_waitqueue_init (&lock->waiting_readers);
    656   gl_waitqueue_init (&lock->waiting_writers);
    657   lock->runcount = 0;
    658   lock->guard.done = 1;
    659 }
    660 
    661 void
    662 glthread_rwlock_rdlock (gl_rwlock_t *lock)
    663 {
    664   if (!lock->guard.done)
    665     {
    666       if (InterlockedIncrement (&lock->guard.started) == 0)
    667 	/* This thread is the first one to need this lock.  Initialize it.  */
    668 	glthread_rwlock_init (lock);
    669       else
    670 	/* Yield the CPU while waiting for another thread to finish
    671 	   initializing this lock.  */
    672 	while (!lock->guard.done)
    673 	  Sleep (0);
    674     }
    675   EnterCriticalSection (&lock->lock);
    676   /* Test whether only readers are currently running, and whether the runcount
    677      field will not overflow.  */
    678   if (!(lock->runcount + 1 > 0))
    679     {
    680       /* This thread has to wait for a while.  Enqueue it among the
    681 	 waiting_readers.  */
    682       HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
    683       if (event != INVALID_HANDLE_VALUE)
    684 	{
    685 	  DWORD result;
    686 	  LeaveCriticalSection (&lock->lock);
    687 	  /* Wait until another thread signals this event.  */
    688 	  result = WaitForSingleObject (event, INFINITE);
    689 	  if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
    690 	    abort ();
    691 	  CloseHandle (event);
    692 	  /* The thread which signalled the event already did the bookkeeping:
    693 	     removed us from the waiting_readers, incremented lock->runcount.  */
    694 	  if (!(lock->runcount > 0))
    695 	    abort ();
    696 	  return;
    697 	}
    698       else
    699 	{
    700 	  /* Allocation failure.  Weird.  */
    701 	  do
    702 	    {
    703 	      LeaveCriticalSection (&lock->lock);
    704 	      Sleep (1);
    705 	      EnterCriticalSection (&lock->lock);
    706 	    }
    707 	  while (!(lock->runcount + 1 > 0));
    708 	}
    709     }
    710   lock->runcount++;
    711   LeaveCriticalSection (&lock->lock);
    712 }
    713 
    714 void
    715 glthread_rwlock_wrlock (gl_rwlock_t *lock)
    716 {
    717   if (!lock->guard.done)
    718     {
    719       if (InterlockedIncrement (&lock->guard.started) == 0)
    720 	/* This thread is the first one to need this lock.  Initialize it.  */
    721 	glthread_rwlock_init (lock);
    722       else
    723 	/* Yield the CPU while waiting for another thread to finish
    724 	   initializing this lock.  */
    725 	while (!lock->guard.done)
    726 	  Sleep (0);
    727     }
    728   EnterCriticalSection (&lock->lock);
    729   /* Test whether no readers or writers are currently running.  */
    730   if (!(lock->runcount == 0))
    731     {
    732       /* This thread has to wait for a while.  Enqueue it among the
    733 	 waiting_writers.  */
    734       HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
    735       if (event != INVALID_HANDLE_VALUE)
    736 	{
    737 	  DWORD result;
    738 	  LeaveCriticalSection (&lock->lock);
    739 	  /* Wait until another thread signals this event.  */
    740 	  result = WaitForSingleObject (event, INFINITE);
    741 	  if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
    742 	    abort ();
    743 	  CloseHandle (event);
    744 	  /* The thread which signalled the event already did the bookkeeping:
    745 	     removed us from the waiting_writers, set lock->runcount = -1.  */
    746 	  if (!(lock->runcount == -1))
    747 	    abort ();
    748 	  return;
    749 	}
    750       else
    751 	{
    752 	  /* Allocation failure.  Weird.  */
    753 	  do
    754 	    {
    755 	      LeaveCriticalSection (&lock->lock);
    756 	      Sleep (1);
    757 	      EnterCriticalSection (&lock->lock);
    758 	    }
    759 	  while (!(lock->runcount == 0));
    760 	}
    761     }
    762   lock->runcount--; /* runcount becomes -1 */
    763   LeaveCriticalSection (&lock->lock);
    764 }
    765 
    766 void
    767 glthread_rwlock_unlock (gl_rwlock_t *lock)
    768 {
    769   if (!lock->guard.done)
    770     abort ();
    771   EnterCriticalSection (&lock->lock);
    772   if (lock->runcount < 0)
    773     {
    774       /* Drop a writer lock.  */
    775       if (!(lock->runcount == -1))
    776 	abort ();
    777       lock->runcount = 0;
    778     }
    779   else
    780     {
    781       /* Drop a reader lock.  */
    782       if (!(lock->runcount > 0))
    783 	abort ();
    784       lock->runcount--;
    785     }
    786   if (lock->runcount == 0)
    787     {
    788       /* POSIX recommends that "write locks shall take precedence over read
    789 	 locks", to avoid "writer starvation".  */
    790       if (lock->waiting_writers.count > 0)
    791 	{
    792 	  /* Wake up one of the waiting writers.  */
    793 	  lock->runcount--;
    794 	  gl_waitqueue_notify_first (&lock->waiting_writers);
    795 	}
    796       else
    797 	{
    798 	  /* Wake up all waiting readers.  */
    799 	  lock->runcount += lock->waiting_readers.count;
    800 	  gl_waitqueue_notify_all (&lock->waiting_readers);
    801 	}
    802     }
    803   LeaveCriticalSection (&lock->lock);
    804 }
    805 
    806 void
    807 glthread_rwlock_destroy (gl_rwlock_t *lock)
    808 {
    809   if (!lock->guard.done)
    810     abort ();
    811   if (lock->runcount != 0)
    812     abort ();
    813   DeleteCriticalSection (&lock->lock);
    814   if (lock->waiting_readers.array != NULL)
    815     free (lock->waiting_readers.array);
    816   if (lock->waiting_writers.array != NULL)
    817     free (lock->waiting_writers.array);
    818   lock->guard.done = 0;
    819 }
    820 
    821 /* --------------------- gl_recursive_lock_t datatype --------------------- */
    822 
    823 void
    824 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
    825 {
    826   lock->owner = 0;
    827   lock->depth = 0;
    828   InitializeCriticalSection (&lock->lock);
    829   lock->guard.done = 1;
    830 }
    831 
    832 void
    833 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
    834 {
    835   if (!lock->guard.done)
    836     {
    837       if (InterlockedIncrement (&lock->guard.started) == 0)
    838 	/* This thread is the first one to need this lock.  Initialize it.  */
    839 	glthread_recursive_lock_init (lock);
    840       else
    841 	/* Yield the CPU while waiting for another thread to finish
    842 	   initializing this lock.  */
    843 	while (!lock->guard.done)
    844 	  Sleep (0);
    845     }
    846   {
    847     DWORD self = GetCurrentThreadId ();
    848     if (lock->owner != self)
    849       {
    850 	EnterCriticalSection (&lock->lock);
    851 	lock->owner = self;
    852       }
    853     if (++(lock->depth) == 0) /* wraparound? */
    854       abort ();
    855   }
    856 }
    857 
    858 void
    859 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
    860 {
    861   if (lock->owner != GetCurrentThreadId ())
    862     abort ();
    863   if (lock->depth == 0)
    864     abort ();
    865   if (--(lock->depth) == 0)
    866     {
    867       lock->owner = 0;
    868       LeaveCriticalSection (&lock->lock);
    869     }
    870 }
    871 
    872 void
    873 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
    874 {
    875   if (lock->owner != 0)
    876     abort ();
    877   DeleteCriticalSection (&lock->lock);
    878   lock->guard.done = 0;
    879 }
    880 
    881 /* -------------------------- gl_once_t datatype -------------------------- */
    882 
    883 void
    884 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
    885 {
    886   if (once_control->inited <= 0)
    887     {
    888       if (InterlockedIncrement (&once_control->started) == 0)
    889 	{
    890 	  /* This thread is the first one to come to this once_control.  */
    891 	  InitializeCriticalSection (&once_control->lock);
    892 	  EnterCriticalSection (&once_control->lock);
    893 	  once_control->inited = 0;
    894 	  initfunction ();
    895 	  once_control->inited = 1;
    896 	  LeaveCriticalSection (&once_control->lock);
    897 	}
    898       else
    899 	{
    900 	  /* Undo last operation.  */
    901 	  InterlockedDecrement (&once_control->started);
    902 	  /* Some other thread has already started the initialization.
    903 	     Yield the CPU while waiting for the other thread to finish
    904 	     initializing and taking the lock.  */
    905 	  while (once_control->inited < 0)
    906 	    Sleep (0);
    907 	  if (once_control->inited <= 0)
    908 	    {
    909 	      /* Take the lock.  This blocks until the other thread has
    910 		 finished calling the initfunction.  */
    911 	      EnterCriticalSection (&once_control->lock);
    912 	      LeaveCriticalSection (&once_control->lock);
    913 	      if (!(once_control->inited > 0))
    914 		abort ();
    915 	    }
    916 	}
    917     }
    918 }
    919 
    920 #endif
    921 
    922 /* ========================================================================= */
    923