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