• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-lib/
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.  */
35static void *
36dummy_thread_func (void *arg)
37{
38  return arg;
39}
40
41int
42glthread_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
77void
78glthread_rwlock_init (gl_rwlock_t *lock)
79{
80  if (pthread_rwlock_init (&lock->rwlock, NULL) != 0)
81    abort ();
82  lock->initialized = 1;
83}
84
85void
86glthread_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
101void
102glthread_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
117void
118glthread_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
126void
127glthread_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
140void
141glthread_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
153void
154glthread_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
176void
177glthread_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
196void
197glthread_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
236void
237glthread_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
255void
256glthread_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
271void
272glthread_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
287void
288glthread_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
296void
297glthread_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
310void
311glthread_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
319void
320glthread_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
333void
334glthread_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
348void
349glthread_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
361static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
362
363int
364glthread_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
395void
396glthread_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
403int
404glthread_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
431void
432glthread_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
440void
441glthread_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
454void
455glthread_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
469void
470glthread_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
480void
481glthread_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
499int
500glthread_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
521void
522glthread_lock_init (gl_lock_t *lock)
523{
524  InitializeCriticalSection (&lock->lock);
525  lock->guard.done = 1;
526}
527
528void
529glthread_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
545void
546glthread_lock_unlock (gl_lock_t *lock)
547{
548  if (!lock->guard.done)
549    abort ();
550  LeaveCriticalSection (&lock->lock);
551}
552
553void
554glthread_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
564static inline void
565gl_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.  */
575static HANDLE
576gl_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.  */
623static inline void
624gl_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.  */
634static inline void
635gl_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
650void
651glthread_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
660void
661glthread_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
713void
714glthread_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
765void
766glthread_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
805void
806glthread_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
822void
823glthread_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
831void
832glthread_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
857void
858glthread_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
871void
872glthread_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
882void
883glthread_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