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 *
dummy_thread_func(void * arg)37 dummy_thread_func (void *arg)
38 {
39   return arg;
40 }
41 
42 int
glthread_in_use(void)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
glthread_rwlock_init(gl_rwlock_t * lock)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
glthread_rwlock_rdlock(gl_rwlock_t * lock)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
glthread_rwlock_wrlock(gl_rwlock_t * lock)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
glthread_rwlock_unlock(gl_rwlock_t * lock)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
glthread_rwlock_destroy(gl_rwlock_t * lock)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
glthread_rwlock_init(gl_rwlock_t * lock)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
glthread_rwlock_rdlock(gl_rwlock_t * lock)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
glthread_rwlock_wrlock(gl_rwlock_t * lock)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
glthread_rwlock_unlock(gl_rwlock_t * lock)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
glthread_rwlock_destroy(gl_rwlock_t * lock)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
glthread_recursive_lock_init(gl_recursive_lock_t * lock)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
glthread_recursive_lock_lock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_unlock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_destroy(gl_recursive_lock_t * lock)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
glthread_recursive_lock_init(gl_recursive_lock_t * lock)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
glthread_recursive_lock_lock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_unlock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_destroy(gl_recursive_lock_t * lock)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
glthread_once_singlethreaded(pthread_once_t * once_control)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
glthread_once_call(void * arg)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
glthread_once_singlethreaded(pth_once_t * once_control)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
glthread_recursive_lock_init(gl_recursive_lock_t * lock)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
glthread_recursive_lock_lock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_unlock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_destroy(gl_recursive_lock_t * lock)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
glthread_once(gl_once_t * once_control,void (* initfunction)(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
glthread_once_singlethreaded(gl_once_t * once_control)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
glthread_lock_init(gl_lock_t * lock)523 glthread_lock_init (gl_lock_t *lock)
524 {
525   InitializeCriticalSection (&lock->lock);
526   lock->guard.done = 1;
527 }
528 
529 void
glthread_lock_lock(gl_lock_t * lock)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
glthread_lock_unlock(gl_lock_t * lock)547 glthread_lock_unlock (gl_lock_t *lock)
548 {
549   if (!lock->guard.done)
550     abort ();
551   LeaveCriticalSection (&lock->lock);
552 }
553 
554 void
glthread_lock_destroy(gl_lock_t * lock)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
gl_waitqueue_init(gl_waitqueue_t * wq)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
gl_waitqueue_add(gl_waitqueue_t * wq)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
gl_waitqueue_notify_first(gl_waitqueue_t * wq)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
gl_waitqueue_notify_all(gl_waitqueue_t * wq)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
glthread_rwlock_init(gl_rwlock_t * lock)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
glthread_rwlock_rdlock(gl_rwlock_t * lock)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
glthread_rwlock_wrlock(gl_rwlock_t * lock)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
glthread_rwlock_unlock(gl_rwlock_t * lock)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
glthread_rwlock_destroy(gl_rwlock_t * lock)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
glthread_recursive_lock_init(gl_recursive_lock_t * lock)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
glthread_recursive_lock_lock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_unlock(gl_recursive_lock_t * lock)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
glthread_recursive_lock_destroy(gl_recursive_lock_t * lock)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
glthread_once(gl_once_t * once_control,void (* initfunction)(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 /* ========================================================================= */
923