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