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