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@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
37int
38glthread_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
49int
50glthread_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
75int
76glthread_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
101int
102glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
103{
104  if (!lock->initialized)
105    return EINVAL;
106  return pthread_rwlock_unlock (&lock->rwlock);
107}
108
109int
110glthread_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
127int
128glthread_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
146int
147glthread_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
175int
176glthread_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
202int
203glthread_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
258int
259glthread_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
283int
284glthread_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
312int
313glthread_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
340int
341glthread_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
366int
367glthread_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
374int
375glthread_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
392int
393glthread_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
405int
406glthread_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
426int
427glthread_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
442int
443glthread_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
454static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
455
456int
457glthread_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
488static void
489glthread_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
496int
497glthread_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
503int
504glthread_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
531int
532glthread_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
544int
545glthread_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
565int
566glthread_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
581int
582glthread_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
591int
592glthread_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
614int
615glthread_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
636void
637glthread_lock_init_func (gl_lock_t *lock)
638{
639  InitializeCriticalSection (&lock->lock);
640  lock->guard.done = 1;
641}
642
643int
644glthread_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
661int
662glthread_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
670int
671glthread_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
685static void
686gl_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.  */
696static HANDLE
697gl_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.  */
746static void
747gl_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.  */
757static void
758gl_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
773void
774glthread_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
783int
784glthread_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
837int
838glthread_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
890int
891glthread_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
934int
935glthread_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
952void
953glthread_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
961int
962glthread_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
991int
992glthread_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
1006int
1007glthread_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
1018void
1019glthread_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