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