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