1 /* Locking in multithreaded situations.
2 Copyright (C) 2005 Free Software Foundation, Inc.
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)
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.
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. */
18 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
19 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
28 /* ========================================================================= */
32 /* Use the POSIX threads library. */
34 # if PTHREAD_IN_USE_DETECTION_HARD
36 /* The function to be executed by a dummy thread. */
38 dummy_thread_func (void *arg)
44 glthread_in_use (void)
47 static int result; /* 1: linked with -lpthread, 0: only with libc */
53 if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0)
54 /* Thread creation failed. */
58 /* Thread creation works. */
60 if (pthread_join (thread, &retval) != 0)
71 /* -------------------------- gl_lock_t datatype -------------------------- */
73 /* ------------------------- gl_rwlock_t datatype ------------------------- */
75 # if HAVE_PTHREAD_RWLOCK
77 # if !defined PTHREAD_RWLOCK_INITIALIZER
80 glthread_rwlock_init (gl_rwlock_t *lock)
82 if (pthread_rwlock_init (&lock->rwlock, NULL) != 0)
84 lock->initialized = 1;
88 glthread_rwlock_rdlock (gl_rwlock_t *lock)
90 if (!lock->initialized)
92 if (pthread_mutex_lock (&lock->guard) != 0)
94 if (!lock->initialized)
95 glthread_rwlock_init (lock);
96 if (pthread_mutex_unlock (&lock->guard) != 0)
99 if (pthread_rwlock_rdlock (&lock->rwlock) != 0)
104 glthread_rwlock_wrlock (gl_rwlock_t *lock)
106 if (!lock->initialized)
108 if (pthread_mutex_lock (&lock->guard) != 0)
110 if (!lock->initialized)
111 glthread_rwlock_init (lock);
112 if (pthread_mutex_unlock (&lock->guard) != 0)
115 if (pthread_rwlock_wrlock (&lock->rwlock) != 0)
120 glthread_rwlock_unlock (gl_rwlock_t *lock)
122 if (!lock->initialized)
124 if (pthread_rwlock_unlock (&lock->rwlock) != 0)
129 glthread_rwlock_destroy (gl_rwlock_t *lock)
131 if (!lock->initialized)
133 if (pthread_rwlock_destroy (&lock->rwlock) != 0)
135 lock->initialized = 0;
143 glthread_rwlock_init (gl_rwlock_t *lock)
145 if (pthread_mutex_init (&lock->lock, NULL) != 0)
147 if (pthread_cond_init (&lock->waiting_readers, NULL) != 0)
149 if (pthread_cond_init (&lock->waiting_writers, NULL) != 0)
151 lock->waiting_writers_count = 0;
156 glthread_rwlock_rdlock (gl_rwlock_t *lock)
158 if (pthread_mutex_lock (&lock->lock) != 0)
160 /* Test whether only readers are currently running, and whether the runcount
161 field will not overflow. */
162 /* POSIX says: "It is implementation-defined whether the calling thread
163 acquires the lock when a writer does not hold the lock and there are
164 writers blocked on the lock." Let's say, no: give the writers a higher
166 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
168 /* This thread has to wait for a while. Enqueue it among the
170 if (pthread_cond_wait (&lock->waiting_readers, &lock->lock) != 0)
174 if (pthread_mutex_unlock (&lock->lock) != 0)
179 glthread_rwlock_wrlock (gl_rwlock_t *lock)
181 if (pthread_mutex_lock (&lock->lock) != 0)
183 /* Test whether no readers or writers are currently running. */
184 while (!(lock->runcount == 0))
186 /* This thread has to wait for a while. Enqueue it among the
188 lock->waiting_writers_count++;
189 if (pthread_cond_wait (&lock->waiting_writers, &lock->lock) != 0)
191 lock->waiting_writers_count--;
193 lock->runcount--; /* runcount becomes -1 */
194 if (pthread_mutex_unlock (&lock->lock) != 0)
199 glthread_rwlock_unlock (gl_rwlock_t *lock)
201 if (pthread_mutex_lock (&lock->lock) != 0)
203 if (lock->runcount < 0)
205 /* Drop a writer lock. */
206 if (!(lock->runcount == -1))
212 /* Drop a reader lock. */
213 if (!(lock->runcount > 0))
217 if (lock->runcount == 0)
219 /* POSIX recommends that "write locks shall take precedence over read
220 locks", to avoid "writer starvation". */
221 if (lock->waiting_writers_count > 0)
223 /* Wake up one of the waiting writers. */
224 if (pthread_cond_signal (&lock->waiting_writers) != 0)
229 /* Wake up all waiting readers. */
230 if (pthread_cond_broadcast (&lock->waiting_readers) != 0)
234 if (pthread_mutex_unlock (&lock->lock) != 0)
239 glthread_rwlock_destroy (gl_rwlock_t *lock)
241 if (pthread_mutex_destroy (&lock->lock) != 0)
243 if (pthread_cond_destroy (&lock->waiting_readers) != 0)
245 if (pthread_cond_destroy (&lock->waiting_writers) != 0)
251 /* --------------------- gl_recursive_lock_t datatype --------------------- */
253 # if HAVE_PTHREAD_MUTEX_RECURSIVE
255 # if !(defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
258 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
260 pthread_mutexattr_t attributes;
262 if (pthread_mutexattr_init (&attributes) != 0)
264 if (pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE) != 0)
266 if (pthread_mutex_init (&lock->recmutex, &attributes) != 0)
268 if (pthread_mutexattr_destroy (&attributes) != 0)
270 lock->initialized = 1;
274 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
276 if (!lock->initialized)
278 if (pthread_mutex_lock (&lock->guard) != 0)
280 if (!lock->initialized)
281 glthread_recursive_lock_init (lock);
282 if (pthread_mutex_unlock (&lock->guard) != 0)
285 if (pthread_mutex_lock (&lock->recmutex) != 0)
290 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
292 if (!lock->initialized)
294 if (pthread_mutex_unlock (&lock->recmutex) != 0)
299 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
301 if (!lock->initialized)
303 if (pthread_mutex_destroy (&lock->recmutex) != 0)
305 lock->initialized = 0;
313 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
315 if (pthread_mutex_init (&lock->mutex, NULL) != 0)
317 lock->owner = (pthread_t) 0;
322 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
324 pthread_t self = pthread_self ();
325 if (lock->owner != self)
327 if (pthread_mutex_lock (&lock->mutex) != 0)
331 if (++(lock->depth) == 0) /* wraparound? */
336 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
338 if (lock->owner != pthread_self ())
340 if (lock->depth == 0)
342 if (--(lock->depth) == 0)
344 lock->owner = (pthread_t) 0;
345 if (pthread_mutex_unlock (&lock->mutex) != 0)
351 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
353 if (lock->owner != (pthread_t) 0)
355 if (pthread_mutex_destroy (&lock->mutex) != 0)
361 /* -------------------------- gl_once_t datatype -------------------------- */
363 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
366 glthread_once_singlethreaded (pthread_once_t *once_control)
368 /* We don't know whether pthread_once_t is an integer type, a floating-point
369 type, a pointer type, or a structure type. */
370 char *firstbyte = (char *)once_control;
371 if (*firstbyte == *(const char *)&fresh_once)
373 /* First time use of once_control. Invert the first byte. */
374 *firstbyte = ~ *(const char *)&fresh_once;
383 /* ========================================================================= */
387 /* Use the GNU Pth threads library. */
389 /* -------------------------- gl_lock_t datatype -------------------------- */
391 /* ------------------------- gl_rwlock_t datatype ------------------------- */
393 /* --------------------- gl_recursive_lock_t datatype --------------------- */
395 /* -------------------------- gl_once_t datatype -------------------------- */
398 glthread_once_call (void *arg)
400 void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
401 void (*initfunction) (void) = *gl_once_temp_addr;
406 glthread_once_singlethreaded (pth_once_t *once_control)
408 /* We know that pth_once_t is an integer type. */
409 if (*once_control == PTH_ONCE_INIT)
411 /* First time use of once_control. Invert the marker. */
412 *once_control = ~ PTH_ONCE_INIT;
421 /* ========================================================================= */
423 #if USE_SOLARIS_THREADS
425 /* Use the old Solaris threads library. */
427 /* -------------------------- gl_lock_t datatype -------------------------- */
429 /* ------------------------- gl_rwlock_t datatype ------------------------- */
431 /* --------------------- gl_recursive_lock_t datatype --------------------- */
434 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
436 if (mutex_init (&lock->mutex, USYNC_THREAD, NULL) != 0)
438 lock->owner = (thread_t) 0;
443 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
445 thread_t self = thr_self ();
446 if (lock->owner != self)
448 if (mutex_lock (&lock->mutex) != 0)
452 if (++(lock->depth) == 0) /* wraparound? */
457 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
459 if (lock->owner != thr_self ())
461 if (lock->depth == 0)
463 if (--(lock->depth) == 0)
465 lock->owner = (thread_t) 0;
466 if (mutex_unlock (&lock->mutex) != 0)
472 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
474 if (lock->owner != (thread_t) 0)
476 if (mutex_destroy (&lock->mutex) != 0)
480 /* -------------------------- gl_once_t datatype -------------------------- */
483 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
485 if (!once_control->inited)
487 /* Use the mutex to guarantee that if another thread is already calling
488 the initfunction, this thread waits until it's finished. */
489 if (mutex_lock (&once_control->mutex) != 0)
491 if (!once_control->inited)
493 once_control->inited = 1;
496 if (mutex_unlock (&once_control->mutex) != 0)
502 glthread_once_singlethreaded (gl_once_t *once_control)
504 /* We know that gl_once_t contains an integer type. */
505 if (!once_control->inited)
507 /* First time use of once_control. Invert the marker. */
508 once_control->inited = ~ 0;
517 /* ========================================================================= */
519 #if USE_WIN32_THREADS
521 /* -------------------------- gl_lock_t datatype -------------------------- */
524 glthread_lock_init (gl_lock_t *lock)
526 InitializeCriticalSection (&lock->lock);
527 lock->guard.done = 1;
531 glthread_lock_lock (gl_lock_t *lock)
533 if (!lock->guard.done)
535 if (InterlockedIncrement (&lock->guard.started) == 0)
536 /* This thread is the first one to need this lock. Initialize it. */
537 glthread_lock_init (lock);
539 /* Yield the CPU while waiting for another thread to finish
540 initializing this lock. */
541 while (!lock->guard.done)
544 EnterCriticalSection (&lock->lock);
548 glthread_lock_unlock (gl_lock_t *lock)
550 if (!lock->guard.done)
552 LeaveCriticalSection (&lock->lock);
556 glthread_lock_destroy (gl_lock_t *lock)
558 if (!lock->guard.done)
560 DeleteCriticalSection (&lock->lock);
561 lock->guard.done = 0;
564 /* ------------------------- gl_rwlock_t datatype ------------------------- */
567 gl_waitqueue_init (gl_waitqueue_t *wq)
575 /* Enqueues the current thread, represented by an event, in a wait queue.
576 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
578 gl_waitqueue_add (gl_waitqueue_t *wq)
583 if (wq->count == wq->alloc)
585 unsigned int new_alloc = 2 * wq->alloc + 1;
587 (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
588 if (new_array == NULL)
589 /* No more memory. */
590 return INVALID_HANDLE_VALUE;
591 /* Now is a good opportunity to rotate the array so that its contents
592 starts at offset 0. */
595 unsigned int old_count = wq->count;
596 unsigned int old_alloc = wq->alloc;
597 unsigned int old_offset = wq->offset;
599 if (old_offset + old_count > old_alloc)
601 unsigned int limit = old_offset + old_count - old_alloc;
602 for (i = 0; i < limit; i++)
603 new_array[old_alloc + i] = new_array[i];
605 for (i = 0; i < old_count; i++)
606 new_array[i] = new_array[old_offset + i];
609 wq->array = new_array;
610 wq->alloc = new_alloc;
612 event = CreateEvent (NULL, TRUE, FALSE, NULL);
613 if (event == INVALID_HANDLE_VALUE)
614 /* No way to allocate an event. */
615 return INVALID_HANDLE_VALUE;
616 index = wq->offset + wq->count;
617 if (index >= wq->alloc)
619 wq->array[index] = event;
624 /* Notifies the first thread from a wait queue and dequeues it. */
626 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
628 SetEvent (wq->array[wq->offset + 0]);
631 if (wq->count == 0 || wq->offset == wq->alloc)
635 /* Notifies all threads from a wait queue and dequeues them all. */
637 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
641 for (i = 0; i < wq->count; i++)
643 unsigned int index = wq->offset + i;
644 if (index >= wq->alloc)
646 SetEvent (wq->array[index]);
653 glthread_rwlock_init (gl_rwlock_t *lock)
655 InitializeCriticalSection (&lock->lock);
656 gl_waitqueue_init (&lock->waiting_readers);
657 gl_waitqueue_init (&lock->waiting_writers);
659 lock->guard.done = 1;
663 glthread_rwlock_rdlock (gl_rwlock_t *lock)
665 if (!lock->guard.done)
667 if (InterlockedIncrement (&lock->guard.started) == 0)
668 /* This thread is the first one to need this lock. Initialize it. */
669 glthread_rwlock_init (lock);
671 /* Yield the CPU while waiting for another thread to finish
672 initializing this lock. */
673 while (!lock->guard.done)
676 EnterCriticalSection (&lock->lock);
677 /* Test whether only readers are currently running, and whether the runcount
678 field will not overflow. */
679 if (!(lock->runcount + 1 > 0))
681 /* This thread has to wait for a while. Enqueue it among the
683 HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
684 if (event != INVALID_HANDLE_VALUE)
687 LeaveCriticalSection (&lock->lock);
688 /* Wait until another thread signals this event. */
689 result = WaitForSingleObject (event, INFINITE);
690 if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
693 /* The thread which signalled the event already did the bookkeeping:
694 removed us from the waiting_readers, incremented lock->runcount. */
695 if (!(lock->runcount > 0))
701 /* Allocation failure. Weird. */
704 LeaveCriticalSection (&lock->lock);
706 EnterCriticalSection (&lock->lock);
708 while (!(lock->runcount + 1 > 0));
712 LeaveCriticalSection (&lock->lock);
716 glthread_rwlock_wrlock (gl_rwlock_t *lock)
718 if (!lock->guard.done)
720 if (InterlockedIncrement (&lock->guard.started) == 0)
721 /* This thread is the first one to need this lock. Initialize it. */
722 glthread_rwlock_init (lock);
724 /* Yield the CPU while waiting for another thread to finish
725 initializing this lock. */
726 while (!lock->guard.done)
729 EnterCriticalSection (&lock->lock);
730 /* Test whether no readers or writers are currently running. */
731 if (!(lock->runcount == 0))
733 /* This thread has to wait for a while. Enqueue it among the
735 HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
736 if (event != INVALID_HANDLE_VALUE)
739 LeaveCriticalSection (&lock->lock);
740 /* Wait until another thread signals this event. */
741 result = WaitForSingleObject (event, INFINITE);
742 if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
745 /* The thread which signalled the event already did the bookkeeping:
746 removed us from the waiting_writers, set lock->runcount = -1. */
747 if (!(lock->runcount == -1))
753 /* Allocation failure. Weird. */
756 LeaveCriticalSection (&lock->lock);
758 EnterCriticalSection (&lock->lock);
760 while (!(lock->runcount == 0));
763 lock->runcount--; /* runcount becomes -1 */
764 LeaveCriticalSection (&lock->lock);
768 glthread_rwlock_unlock (gl_rwlock_t *lock)
770 if (!lock->guard.done)
772 EnterCriticalSection (&lock->lock);
773 if (lock->runcount < 0)
775 /* Drop a writer lock. */
776 if (!(lock->runcount == -1))
782 /* Drop a reader lock. */
783 if (!(lock->runcount > 0))
787 if (lock->runcount == 0)
789 /* POSIX recommends that "write locks shall take precedence over read
790 locks", to avoid "writer starvation". */
791 if (lock->waiting_writers.count > 0)
793 /* Wake up one of the waiting writers. */
795 gl_waitqueue_notify_first (&lock->waiting_writers);
799 /* Wake up all waiting readers. */
800 lock->runcount += lock->waiting_readers.count;
801 gl_waitqueue_notify_all (&lock->waiting_readers);
804 LeaveCriticalSection (&lock->lock);
808 glthread_rwlock_destroy (gl_rwlock_t *lock)
810 if (!lock->guard.done)
812 if (lock->runcount != 0)
814 DeleteCriticalSection (&lock->lock);
815 if (lock->waiting_readers.array != NULL)
816 free (lock->waiting_readers.array);
817 if (lock->waiting_writers.array != NULL)
818 free (lock->waiting_writers.array);
819 lock->guard.done = 0;
822 /* --------------------- gl_recursive_lock_t datatype --------------------- */
825 glthread_recursive_lock_init (gl_recursive_lock_t *lock)
829 InitializeCriticalSection (&lock->lock);
830 lock->guard.done = 1;
834 glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
836 if (!lock->guard.done)
838 if (InterlockedIncrement (&lock->guard.started) == 0)
839 /* This thread is the first one to need this lock. Initialize it. */
840 glthread_recursive_lock_init (lock);
842 /* Yield the CPU while waiting for another thread to finish
843 initializing this lock. */
844 while (!lock->guard.done)
848 DWORD self = GetCurrentThreadId ();
849 if (lock->owner != self)
851 EnterCriticalSection (&lock->lock);
854 if (++(lock->depth) == 0) /* wraparound? */
860 glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
862 if (lock->owner != GetCurrentThreadId ())
864 if (lock->depth == 0)
866 if (--(lock->depth) == 0)
869 LeaveCriticalSection (&lock->lock);
874 glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
876 if (lock->owner != 0)
878 DeleteCriticalSection (&lock->lock);
879 lock->guard.done = 0;
882 /* -------------------------- gl_once_t datatype -------------------------- */
885 glthread_once (gl_once_t *once_control, void (*initfunction) (void))
887 if (once_control->inited <= 0)
889 if (InterlockedIncrement (&once_control->started) == 0)
891 /* This thread is the first one to come to this once_control. */
892 InitializeCriticalSection (&once_control->lock);
893 EnterCriticalSection (&once_control->lock);
894 once_control->inited = 0;
896 once_control->inited = 1;
897 LeaveCriticalSection (&once_control->lock);
901 /* Undo last operation. */
902 InterlockedDecrement (&once_control->started);
903 /* Some other thread has already started the initialization.
904 Yield the CPU while waiting for the other thread to finish
905 initializing and taking the lock. */
906 while (once_control->inited < 0)
908 if (once_control->inited <= 0)
910 /* Take the lock. This blocks until the other thread has
911 finished calling the initfunction. */
912 EnterCriticalSection (&once_control->lock);
913 LeaveCriticalSection (&once_control->lock);
914 if (!(once_control->inited > 0))
923 /* ========================================================================= */