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