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