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