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