xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/gnulib-lib/lock.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* Locking in multithreaded situations.
2    Copyright (C) 2005-2006 Free Software Foundation, Inc.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
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->recmutex, &attributes) != 0)
265     abort ();
266   if (pthread_mutexattr_destroy (&attributes) != 0)
267     abort ();
268   lock->initialized = 1;
269 }
270 
271 void
272 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
273 {
274   if (!lock->initialized)
275     {
276       if (pthread_mutex_lock (&lock->guard) != 0)
277 	abort ();
278       if (!lock->initialized)
279 	glthread_recursive_lock_init (lock);
280       if (pthread_mutex_unlock (&lock->guard) != 0)
281 	abort ();
282     }
283   if (pthread_mutex_lock (&lock->recmutex) != 0)
284     abort ();
285 }
286 
287 void
288 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
289 {
290   if (!lock->initialized)
291     abort ();
292   if (pthread_mutex_unlock (&lock->recmutex) != 0)
293     abort ();
294 }
295 
296 void
297 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
298 {
299   if (!lock->initialized)
300     abort ();
301   if (pthread_mutex_destroy (&lock->recmutex) != 0)
302     abort ();
303   lock->initialized = 0;
304 }
305 
306 #  endif
307 
308 # else
309 
310 void
311 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
312 {
313   if (pthread_mutex_init (&lock->mutex, NULL) != 0)
314     abort ();
315   lock->owner = (pthread_t) 0;
316   lock->depth = 0;
317 }
318 
319 void
320 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
321 {
322   pthread_t self = pthread_self ();
323   if (lock->owner != self)
324     {
325       if (pthread_mutex_lock (&lock->mutex) != 0)
326 	abort ();
327       lock->owner = self;
328     }
329   if (++(lock->depth) == 0) /* wraparound? */
330     abort ();
331 }
332 
333 void
334 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
335 {
336   if (lock->owner != pthread_self ())
337     abort ();
338   if (lock->depth == 0)
339     abort ();
340   if (--(lock->depth) == 0)
341     {
342       lock->owner = (pthread_t) 0;
343       if (pthread_mutex_unlock (&lock->mutex) != 0)
344 	abort ();
345     }
346 }
347 
348 void
349 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
350 {
351   if (lock->owner != (pthread_t) 0)
352     abort ();
353   if (pthread_mutex_destroy (&lock->mutex) != 0)
354     abort ();
355 }
356 
357 # endif
358 
359 /* -------------------------- gl_once_t datatype -------------------------- */
360 
361 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
362 
363 int
364 glthread_once_singlethreaded (pthread_once_t *once_control)
365 {
366   /* We don't know whether pthread_once_t is an integer type, a floating-point
367      type, a pointer type, or a structure type.  */
368   char *firstbyte = (char *)once_control;
369   if (*firstbyte == *(const char *)&fresh_once)
370     {
371       /* First time use of once_control.  Invert the first byte.  */
372       *firstbyte = ~ *(const char *)&fresh_once;
373       return 1;
374     }
375   else
376     return 0;
377 }
378 
379 #endif
380 
381 /* ========================================================================= */
382 
383 #if USE_PTH_THREADS
384 
385 /* Use the GNU Pth threads library.  */
386 
387 /* -------------------------- gl_lock_t datatype -------------------------- */
388 
389 /* ------------------------- gl_rwlock_t datatype ------------------------- */
390 
391 /* --------------------- gl_recursive_lock_t datatype --------------------- */
392 
393 /* -------------------------- gl_once_t datatype -------------------------- */
394 
395 void
396 glthread_once_call (void *arg)
397 {
398   void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
399   void (*initfunction) (void) = *gl_once_temp_addr;
400   initfunction ();
401 }
402 
403 int
404 glthread_once_singlethreaded (pth_once_t *once_control)
405 {
406   /* We know that pth_once_t is an integer type.  */
407   if (*once_control == PTH_ONCE_INIT)
408     {
409       /* First time use of once_control.  Invert the marker.  */
410       *once_control = ~ PTH_ONCE_INIT;
411       return 1;
412     }
413   else
414     return 0;
415 }
416 
417 #endif
418 
419 /* ========================================================================= */
420 
421 #if USE_SOLARIS_THREADS
422 
423 /* Use the old Solaris threads library.  */
424 
425 /* -------------------------- gl_lock_t datatype -------------------------- */
426 
427 /* ------------------------- gl_rwlock_t datatype ------------------------- */
428 
429 /* --------------------- gl_recursive_lock_t datatype --------------------- */
430 
431 void
432 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
433 {
434   if (mutex_init (&lock->mutex, USYNC_THREAD, NULL) != 0)
435     abort ();
436   lock->owner = (thread_t) 0;
437   lock->depth = 0;
438 }
439 
440 void
441 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
442 {
443   thread_t self = thr_self ();
444   if (lock->owner != self)
445     {
446       if (mutex_lock (&lock->mutex) != 0)
447 	abort ();
448       lock->owner = self;
449     }
450   if (++(lock->depth) == 0) /* wraparound? */
451     abort ();
452 }
453 
454 void
455 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
456 {
457   if (lock->owner != thr_self ())
458     abort ();
459   if (lock->depth == 0)
460     abort ();
461   if (--(lock->depth) == 0)
462     {
463       lock->owner = (thread_t) 0;
464       if (mutex_unlock (&lock->mutex) != 0)
465 	abort ();
466     }
467 }
468 
469 void
470 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
471 {
472   if (lock->owner != (thread_t) 0)
473     abort ();
474   if (mutex_destroy (&lock->mutex) != 0)
475     abort ();
476 }
477 
478 /* -------------------------- gl_once_t datatype -------------------------- */
479 
480 void
481 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
482 {
483   if (!once_control->inited)
484     {
485       /* Use the mutex to guarantee that if another thread is already calling
486 	 the initfunction, this thread waits until it's finished.  */
487       if (mutex_lock (&once_control->mutex) != 0)
488 	abort ();
489       if (!once_control->inited)
490 	{
491 	  once_control->inited = 1;
492 	  initfunction ();
493 	}
494       if (mutex_unlock (&once_control->mutex) != 0)
495 	abort ();
496     }
497 }
498 
499 int
500 glthread_once_singlethreaded (gl_once_t *once_control)
501 {
502   /* We know that gl_once_t contains an integer type.  */
503   if (!once_control->inited)
504     {
505       /* First time use of once_control.  Invert the marker.  */
506       once_control->inited = ~ 0;
507       return 1;
508     }
509   else
510     return 0;
511 }
512 
513 #endif
514 
515 /* ========================================================================= */
516 
517 #if USE_WIN32_THREADS
518 
519 /* -------------------------- gl_lock_t datatype -------------------------- */
520 
521 void
522 glthread_lock_init (gl_lock_t *lock)
523 {
524   InitializeCriticalSection (&lock->lock);
525   lock->guard.done = 1;
526 }
527 
528 void
529 glthread_lock_lock (gl_lock_t *lock)
530 {
531   if (!lock->guard.done)
532     {
533       if (InterlockedIncrement (&lock->guard.started) == 0)
534 	/* This thread is the first one to need this lock.  Initialize it.  */
535 	glthread_lock_init (lock);
536       else
537 	/* Yield the CPU while waiting for another thread to finish
538 	   initializing this lock.  */
539 	while (!lock->guard.done)
540 	  Sleep (0);
541     }
542   EnterCriticalSection (&lock->lock);
543 }
544 
545 void
546 glthread_lock_unlock (gl_lock_t *lock)
547 {
548   if (!lock->guard.done)
549     abort ();
550   LeaveCriticalSection (&lock->lock);
551 }
552 
553 void
554 glthread_lock_destroy (gl_lock_t *lock)
555 {
556   if (!lock->guard.done)
557     abort ();
558   DeleteCriticalSection (&lock->lock);
559   lock->guard.done = 0;
560 }
561 
562 /* ------------------------- gl_rwlock_t datatype ------------------------- */
563 
564 static inline void
565 gl_waitqueue_init (gl_waitqueue_t *wq)
566 {
567   wq->array = NULL;
568   wq->count = 0;
569   wq->alloc = 0;
570   wq->offset = 0;
571 }
572 
573 /* Enqueues the current thread, represented by an event, in a wait queue.
574    Returns INVALID_HANDLE_VALUE if an allocation failure occurs.  */
575 static HANDLE
576 gl_waitqueue_add (gl_waitqueue_t *wq)
577 {
578   HANDLE event;
579   unsigned int index;
580 
581   if (wq->count == wq->alloc)
582     {
583       unsigned int new_alloc = 2 * wq->alloc + 1;
584       HANDLE *new_array =
585 	(HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
586       if (new_array == NULL)
587 	/* No more memory.  */
588 	return INVALID_HANDLE_VALUE;
589       /* Now is a good opportunity to rotate the array so that its contents
590 	 starts at offset 0.  */
591       if (wq->offset > 0)
592 	{
593 	  unsigned int old_count = wq->count;
594 	  unsigned int old_alloc = wq->alloc;
595 	  unsigned int old_offset = wq->offset;
596 	  unsigned int i;
597 	  if (old_offset + old_count > old_alloc)
598 	    {
599 	      unsigned int limit = old_offset + old_count - old_alloc;
600 	      for (i = 0; i < limit; i++)
601 		new_array[old_alloc + i] = new_array[i];
602 	    }
603 	  for (i = 0; i < old_count; i++)
604 	    new_array[i] = new_array[old_offset + i];
605 	  wq->offset = 0;
606 	}
607       wq->array = new_array;
608       wq->alloc = new_alloc;
609     }
610   event = CreateEvent (NULL, TRUE, FALSE, NULL);
611   if (event == INVALID_HANDLE_VALUE)
612     /* No way to allocate an event.  */
613     return INVALID_HANDLE_VALUE;
614   index = wq->offset + wq->count;
615   if (index >= wq->alloc)
616     index -= wq->alloc;
617   wq->array[index] = event;
618   wq->count++;
619   return event;
620 }
621 
622 /* Notifies the first thread from a wait queue and dequeues it.  */
623 static inline void
624 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
625 {
626   SetEvent (wq->array[wq->offset + 0]);
627   wq->offset++;
628   wq->count--;
629   if (wq->count == 0 || wq->offset == wq->alloc)
630     wq->offset = 0;
631 }
632 
633 /* Notifies all threads from a wait queue and dequeues them all.  */
634 static inline void
635 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
636 {
637   unsigned int i;
638 
639   for (i = 0; i < wq->count; i++)
640     {
641       unsigned int index = wq->offset + i;
642       if (index >= wq->alloc)
643 	index -= wq->alloc;
644       SetEvent (wq->array[index]);
645     }
646   wq->count = 0;
647   wq->offset = 0;
648 }
649 
650 void
651 glthread_rwlock_init (gl_rwlock_t *lock)
652 {
653   InitializeCriticalSection (&lock->lock);
654   gl_waitqueue_init (&lock->waiting_readers);
655   gl_waitqueue_init (&lock->waiting_writers);
656   lock->runcount = 0;
657   lock->guard.done = 1;
658 }
659 
660 void
661 glthread_rwlock_rdlock (gl_rwlock_t *lock)
662 {
663   if (!lock->guard.done)
664     {
665       if (InterlockedIncrement (&lock->guard.started) == 0)
666 	/* This thread is the first one to need this lock.  Initialize it.  */
667 	glthread_rwlock_init (lock);
668       else
669 	/* Yield the CPU while waiting for another thread to finish
670 	   initializing this lock.  */
671 	while (!lock->guard.done)
672 	  Sleep (0);
673     }
674   EnterCriticalSection (&lock->lock);
675   /* Test whether only readers are currently running, and whether the runcount
676      field will not overflow.  */
677   if (!(lock->runcount + 1 > 0))
678     {
679       /* This thread has to wait for a while.  Enqueue it among the
680 	 waiting_readers.  */
681       HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
682       if (event != INVALID_HANDLE_VALUE)
683 	{
684 	  DWORD result;
685 	  LeaveCriticalSection (&lock->lock);
686 	  /* Wait until another thread signals this event.  */
687 	  result = WaitForSingleObject (event, INFINITE);
688 	  if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
689 	    abort ();
690 	  CloseHandle (event);
691 	  /* The thread which signalled the event already did the bookkeeping:
692 	     removed us from the waiting_readers, incremented lock->runcount.  */
693 	  if (!(lock->runcount > 0))
694 	    abort ();
695 	  return;
696 	}
697       else
698 	{
699 	  /* Allocation failure.  Weird.  */
700 	  do
701 	    {
702 	      LeaveCriticalSection (&lock->lock);
703 	      Sleep (1);
704 	      EnterCriticalSection (&lock->lock);
705 	    }
706 	  while (!(lock->runcount + 1 > 0));
707 	}
708     }
709   lock->runcount++;
710   LeaveCriticalSection (&lock->lock);
711 }
712 
713 void
714 glthread_rwlock_wrlock (gl_rwlock_t *lock)
715 {
716   if (!lock->guard.done)
717     {
718       if (InterlockedIncrement (&lock->guard.started) == 0)
719 	/* This thread is the first one to need this lock.  Initialize it.  */
720 	glthread_rwlock_init (lock);
721       else
722 	/* Yield the CPU while waiting for another thread to finish
723 	   initializing this lock.  */
724 	while (!lock->guard.done)
725 	  Sleep (0);
726     }
727   EnterCriticalSection (&lock->lock);
728   /* Test whether no readers or writers are currently running.  */
729   if (!(lock->runcount == 0))
730     {
731       /* This thread has to wait for a while.  Enqueue it among the
732 	 waiting_writers.  */
733       HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
734       if (event != INVALID_HANDLE_VALUE)
735 	{
736 	  DWORD result;
737 	  LeaveCriticalSection (&lock->lock);
738 	  /* Wait until another thread signals this event.  */
739 	  result = WaitForSingleObject (event, INFINITE);
740 	  if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
741 	    abort ();
742 	  CloseHandle (event);
743 	  /* The thread which signalled the event already did the bookkeeping:
744 	     removed us from the waiting_writers, set lock->runcount = -1.  */
745 	  if (!(lock->runcount == -1))
746 	    abort ();
747 	  return;
748 	}
749       else
750 	{
751 	  /* Allocation failure.  Weird.  */
752 	  do
753 	    {
754 	      LeaveCriticalSection (&lock->lock);
755 	      Sleep (1);
756 	      EnterCriticalSection (&lock->lock);
757 	    }
758 	  while (!(lock->runcount == 0));
759 	}
760     }
761   lock->runcount--; /* runcount becomes -1 */
762   LeaveCriticalSection (&lock->lock);
763 }
764 
765 void
766 glthread_rwlock_unlock (gl_rwlock_t *lock)
767 {
768   if (!lock->guard.done)
769     abort ();
770   EnterCriticalSection (&lock->lock);
771   if (lock->runcount < 0)
772     {
773       /* Drop a writer lock.  */
774       if (!(lock->runcount == -1))
775 	abort ();
776       lock->runcount = 0;
777     }
778   else
779     {
780       /* Drop a reader lock.  */
781       if (!(lock->runcount > 0))
782 	abort ();
783       lock->runcount--;
784     }
785   if (lock->runcount == 0)
786     {
787       /* POSIX recommends that "write locks shall take precedence over read
788 	 locks", to avoid "writer starvation".  */
789       if (lock->waiting_writers.count > 0)
790 	{
791 	  /* Wake up one of the waiting writers.  */
792 	  lock->runcount--;
793 	  gl_waitqueue_notify_first (&lock->waiting_writers);
794 	}
795       else
796 	{
797 	  /* Wake up all waiting readers.  */
798 	  lock->runcount += lock->waiting_readers.count;
799 	  gl_waitqueue_notify_all (&lock->waiting_readers);
800 	}
801     }
802   LeaveCriticalSection (&lock->lock);
803 }
804 
805 void
806 glthread_rwlock_destroy (gl_rwlock_t *lock)
807 {
808   if (!lock->guard.done)
809     abort ();
810   if (lock->runcount != 0)
811     abort ();
812   DeleteCriticalSection (&lock->lock);
813   if (lock->waiting_readers.array != NULL)
814     free (lock->waiting_readers.array);
815   if (lock->waiting_writers.array != NULL)
816     free (lock->waiting_writers.array);
817   lock->guard.done = 0;
818 }
819 
820 /* --------------------- gl_recursive_lock_t datatype --------------------- */
821 
822 void
823 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
824 {
825   lock->owner = 0;
826   lock->depth = 0;
827   InitializeCriticalSection (&lock->lock);
828   lock->guard.done = 1;
829 }
830 
831 void
832 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
833 {
834   if (!lock->guard.done)
835     {
836       if (InterlockedIncrement (&lock->guard.started) == 0)
837 	/* This thread is the first one to need this lock.  Initialize it.  */
838 	glthread_recursive_lock_init (lock);
839       else
840 	/* Yield the CPU while waiting for another thread to finish
841 	   initializing this lock.  */
842 	while (!lock->guard.done)
843 	  Sleep (0);
844     }
845   {
846     DWORD self = GetCurrentThreadId ();
847     if (lock->owner != self)
848       {
849 	EnterCriticalSection (&lock->lock);
850 	lock->owner = self;
851       }
852     if (++(lock->depth) == 0) /* wraparound? */
853       abort ();
854   }
855 }
856 
857 void
858 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
859 {
860   if (lock->owner != GetCurrentThreadId ())
861     abort ();
862   if (lock->depth == 0)
863     abort ();
864   if (--(lock->depth) == 0)
865     {
866       lock->owner = 0;
867       LeaveCriticalSection (&lock->lock);
868     }
869 }
870 
871 void
872 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
873 {
874   if (lock->owner != 0)
875     abort ();
876   DeleteCriticalSection (&lock->lock);
877   lock->guard.done = 0;
878 }
879 
880 /* -------------------------- gl_once_t datatype -------------------------- */
881 
882 void
883 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
884 {
885   if (once_control->inited <= 0)
886     {
887       if (InterlockedIncrement (&once_control->started) == 0)
888 	{
889 	  /* This thread is the first one to come to this once_control.  */
890 	  InitializeCriticalSection (&once_control->lock);
891 	  EnterCriticalSection (&once_control->lock);
892 	  once_control->inited = 0;
893 	  initfunction ();
894 	  once_control->inited = 1;
895 	  LeaveCriticalSection (&once_control->lock);
896 	}
897       else
898 	{
899 	  /* Undo last operation.  */
900 	  InterlockedDecrement (&once_control->started);
901 	  /* Some other thread has already started the initialization.
902 	     Yield the CPU while waiting for the other thread to finish
903 	     initializing and taking the lock.  */
904 	  while (once_control->inited < 0)
905 	    Sleep (0);
906 	  if (once_control->inited <= 0)
907 	    {
908 	      /* Take the lock.  This blocks until the other thread has
909 		 finished calling the initfunction.  */
910 	      EnterCriticalSection (&once_control->lock);
911 	      LeaveCriticalSection (&once_control->lock);
912 	      if (!(once_control->inited > 0))
913 		abort ();
914 	    }
915 	}
916     }
917 }
918 
919 #endif
920 
921 /* ========================================================================= */
922