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