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