1 /* Locking in multithreaded situations.
2 Copyright (C) 2005-2008 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,
24 #include "glthread/lock.h"
26 /* ========================================================================= */
30 /* Use the POSIX threads library. */
32 # if PTHREAD_IN_USE_DETECTION_HARD
34 /* The function to be executed by a dummy thread. */
36 dummy_thread_func (void *arg)
42 glthread_in_use (void)
45 static int result; /* 1: linked with -lpthread, 0: only with libc */
51 if (pthread_create (&thread, NULL, dummy_thread_func, NULL) != 0)
52 /* Thread creation failed. */
56 /* Thread creation works. */
58 if (pthread_join (thread, &retval) != 0)
69 /* -------------------------- gl_lock_t datatype -------------------------- */
71 /* ------------------------- gl_rwlock_t datatype ------------------------- */
73 # if HAVE_PTHREAD_RWLOCK
75 # if !defined PTHREAD_RWLOCK_INITIALIZER
78 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
82 err = pthread_rwlock_init (&lock->rwlock, NULL);
85 lock->initialized = 1;
90 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
92 if (!lock->initialized)
96 err = pthread_mutex_lock (&lock->guard);
99 if (!lock->initialized)
101 err = glthread_rwlock_init_multithreaded (lock);
104 pthread_mutex_unlock (&lock->guard);
108 err = pthread_mutex_unlock (&lock->guard);
112 return pthread_rwlock_rdlock (&lock->rwlock);
116 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
118 if (!lock->initialized)
122 err = pthread_mutex_lock (&lock->guard);
125 if (!lock->initialized)
127 err = glthread_rwlock_init_multithreaded (lock);
130 pthread_mutex_unlock (&lock->guard);
134 err = pthread_mutex_unlock (&lock->guard);
138 return pthread_rwlock_wrlock (&lock->rwlock);
142 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
144 if (!lock->initialized)
146 return pthread_rwlock_unlock (&lock->rwlock);
150 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
154 if (!lock->initialized)
156 err = pthread_rwlock_destroy (&lock->rwlock);
159 lock->initialized = 0;
168 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
172 err = pthread_mutex_init (&lock->lock, NULL);
175 err = pthread_cond_init (&lock->waiting_readers, NULL);
178 err = pthread_cond_init (&lock->waiting_writers, NULL);
181 lock->waiting_writers_count = 0;
187 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
191 err = pthread_mutex_lock (&lock->lock);
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
200 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
202 /* This thread has to wait for a while. Enqueue it among the
204 err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
207 pthread_mutex_unlock (&lock->lock);
212 return pthread_mutex_unlock (&lock->lock);
216 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
220 err = pthread_mutex_lock (&lock->lock);
223 /* Test whether no readers or writers are currently running. */
224 while (!(lock->runcount == 0))
226 /* This thread has to wait for a while. Enqueue it among the
228 lock->waiting_writers_count++;
229 err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
232 lock->waiting_writers_count--;
233 pthread_mutex_unlock (&lock->lock);
236 lock->waiting_writers_count--;
238 lock->runcount--; /* runcount becomes -1 */
239 return pthread_mutex_unlock (&lock->lock);
243 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
247 err = pthread_mutex_lock (&lock->lock);
250 if (lock->runcount < 0)
252 /* Drop a writer lock. */
253 if (!(lock->runcount == -1))
255 pthread_mutex_unlock (&lock->lock);
262 /* Drop a reader lock. */
263 if (!(lock->runcount > 0))
265 pthread_mutex_unlock (&lock->lock);
270 if (lock->runcount == 0)
272 /* POSIX recommends that "write locks shall take precedence over read
273 locks", to avoid "writer starvation". */
274 if (lock->waiting_writers_count > 0)
276 /* Wake up one of the waiting writers. */
277 err = pthread_cond_signal (&lock->waiting_writers);
280 pthread_mutex_unlock (&lock->lock);
286 /* Wake up all waiting readers. */
287 err = pthread_cond_broadcast (&lock->waiting_readers);
290 pthread_mutex_unlock (&lock->lock);
295 return pthread_mutex_unlock (&lock->lock);
299 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
303 err = pthread_mutex_destroy (&lock->lock);
306 err = pthread_cond_destroy (&lock->waiting_readers);
309 err = pthread_cond_destroy (&lock->waiting_writers);
317 /* --------------------- gl_recursive_lock_t datatype --------------------- */
319 # if HAVE_PTHREAD_MUTEX_RECURSIVE
321 # if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
324 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
326 pthread_mutexattr_t attributes;
329 err = pthread_mutexattr_init (&attributes);
332 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
335 pthread_mutexattr_destroy (&attributes);
338 err = pthread_mutex_init (lock, &attributes);
341 pthread_mutexattr_destroy (&attributes);
344 err = pthread_mutexattr_destroy (&attributes);
353 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
355 pthread_mutexattr_t attributes;
358 err = pthread_mutexattr_init (&attributes);
361 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
364 pthread_mutexattr_destroy (&attributes);
367 err = pthread_mutex_init (&lock->recmutex, &attributes);
370 pthread_mutexattr_destroy (&attributes);
373 err = pthread_mutexattr_destroy (&attributes);
376 lock->initialized = 1;
381 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
383 if (!lock->initialized)
387 err = pthread_mutex_lock (&lock->guard);
390 if (!lock->initialized)
392 err = glthread_recursive_lock_init_multithreaded (lock);
395 pthread_mutex_unlock (&lock->guard);
399 err = pthread_mutex_unlock (&lock->guard);
403 return pthread_mutex_lock (&lock->recmutex);
407 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
409 if (!lock->initialized)
411 return pthread_mutex_unlock (&lock->recmutex);
415 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
419 if (!lock->initialized)
421 err = pthread_mutex_destroy (&lock->recmutex);
424 lock->initialized = 0;
433 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
437 err = pthread_mutex_init (&lock->mutex, NULL);
440 lock->owner = (pthread_t) 0;
446 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
448 pthread_t self = pthread_self ();
449 if (lock->owner != self)
453 err = pthread_mutex_lock (&lock->mutex);
458 if (++(lock->depth) == 0) /* wraparound? */
467 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
469 if (lock->owner != pthread_self ())
471 if (lock->depth == 0)
473 if (--(lock->depth) == 0)
475 lock->owner = (pthread_t) 0;
476 return pthread_mutex_unlock (&lock->mutex);
483 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
485 if (lock->owner != (pthread_t) 0)
487 return (pthread_mutex_destroy (&lock->mutex);
492 /* -------------------------- gl_once_t datatype -------------------------- */
494 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
497 glthread_once_singlethreaded (pthread_once_t *once_control)
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)
504 /* First time use of once_control. Invert the first byte. */
505 *firstbyte = ~ *(const char *)&fresh_once;
514 /* ========================================================================= */
518 /* Use the GNU Pth threads library. */
520 /* -------------------------- gl_lock_t datatype -------------------------- */
522 /* ------------------------- gl_rwlock_t datatype ------------------------- */
524 /* --------------------- gl_recursive_lock_t datatype --------------------- */
526 /* -------------------------- gl_once_t datatype -------------------------- */
529 glthread_once_call (void *arg)
531 void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
532 void (*initfunction) (void) = *gl_once_temp_addr;
537 glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
539 void (*temp) (void) = initfunction;
540 return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
544 glthread_once_singlethreaded (pth_once_t *once_control)
546 /* We know that pth_once_t is an integer type. */
547 if (*once_control == PTH_ONCE_INIT)
549 /* First time use of once_control. Invert the marker. */
550 *once_control = ~ PTH_ONCE_INIT;
559 /* ========================================================================= */
561 #if USE_SOLARIS_THREADS
563 /* Use the old Solaris threads library. */
565 /* -------------------------- gl_lock_t datatype -------------------------- */
567 /* ------------------------- gl_rwlock_t datatype ------------------------- */
569 /* --------------------- gl_recursive_lock_t datatype --------------------- */
572 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
576 err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
579 lock->owner = (thread_t) 0;
585 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
587 thread_t self = thr_self ();
588 if (lock->owner != self)
592 err = mutex_lock (&lock->mutex);
597 if (++(lock->depth) == 0) /* wraparound? */
606 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
608 if (lock->owner != thr_self ())
610 if (lock->depth == 0)
612 if (--(lock->depth) == 0)
614 lock->owner = (thread_t) 0;
615 return mutex_unlock (&lock->mutex);
622 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
624 if (lock->owner != (thread_t) 0)
626 return mutex_destroy (&lock->mutex);
629 /* -------------------------- gl_once_t datatype -------------------------- */
632 glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
634 if (!once_control->inited)
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);
643 if (!once_control->inited)
645 once_control->inited = 1;
648 return mutex_unlock (&once_control->mutex);
655 glthread_once_singlethreaded (gl_once_t *once_control)
657 /* We know that gl_once_t contains an integer type. */
658 if (!once_control->inited)
660 /* First time use of once_control. Invert the marker. */
661 once_control->inited = ~ 0;
670 /* ========================================================================= */
672 #if USE_WIN32_THREADS
674 /* -------------------------- gl_lock_t datatype -------------------------- */
677 glthread_lock_init_func (gl_lock_t *lock)
679 InitializeCriticalSection (&lock->lock);
680 lock->guard.done = 1;
684 glthread_lock_lock_func (gl_lock_t *lock)
686 if (!lock->guard.done)
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);
692 /* Yield the CPU while waiting for another thread to finish
693 initializing this lock. */
694 while (!lock->guard.done)
697 EnterCriticalSection (&lock->lock);
702 glthread_lock_unlock_func (gl_lock_t *lock)
704 if (!lock->guard.done)
706 LeaveCriticalSection (&lock->lock);
711 glthread_lock_destroy_func (gl_lock_t *lock)
713 if (!lock->guard.done)
715 DeleteCriticalSection (&lock->lock);
716 lock->guard.done = 0;
720 /* ------------------------- gl_rwlock_t datatype ------------------------- */
723 gl_waitqueue_init (gl_waitqueue_t *wq)
731 /* Enqueues the current thread, represented by an event, in a wait queue.
732 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
734 gl_waitqueue_add (gl_waitqueue_t *wq)
739 if (wq->count == wq->alloc)
741 unsigned int new_alloc = 2 * wq->alloc + 1;
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. */
751 unsigned int old_count = wq->count;
752 unsigned int old_alloc = wq->alloc;
753 unsigned int old_offset = wq->offset;
755 if (old_offset + old_count > old_alloc)
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];
761 for (i = 0; i < old_count; i++)
762 new_array[i] = new_array[old_offset + i];
765 wq->array = new_array;
766 wq->alloc = new_alloc;
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)
775 wq->array[index] = event;
780 /* Notifies the first thread from a wait queue and dequeues it. */
782 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
784 SetEvent (wq->array[wq->offset + 0]);
787 if (wq->count == 0 || wq->offset == wq->alloc)
791 /* Notifies all threads from a wait queue and dequeues them all. */
793 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
797 for (i = 0; i < wq->count; i++)
799 unsigned int index = wq->offset + i;
800 if (index >= wq->alloc)
802 SetEvent (wq->array[index]);
809 glthread_rwlock_init_func (gl_rwlock_t *lock)
811 InitializeCriticalSection (&lock->lock);
812 gl_waitqueue_init (&lock->waiting_readers);
813 gl_waitqueue_init (&lock->waiting_writers);
815 lock->guard.done = 1;
819 glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
821 if (!lock->guard.done)
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);
827 /* Yield the CPU while waiting for another thread to finish
828 initializing this lock. */
829 while (!lock->guard.done)
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))
837 /* This thread has to wait for a while. Enqueue it among the
839 HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
840 if (event != INVALID_HANDLE_VALUE)
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)
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))
857 /* Allocation failure. Weird. */
860 LeaveCriticalSection (&lock->lock);
862 EnterCriticalSection (&lock->lock);
864 while (!(lock->runcount + 1 > 0));
868 LeaveCriticalSection (&lock->lock);
873 glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
875 if (!lock->guard.done)
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);
881 /* Yield the CPU while waiting for another thread to finish
882 initializing this lock. */
883 while (!lock->guard.done)
886 EnterCriticalSection (&lock->lock);
887 /* Test whether no readers or writers are currently running. */
888 if (!(lock->runcount == 0))
890 /* This thread has to wait for a while. Enqueue it among the
892 HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
893 if (event != INVALID_HANDLE_VALUE)
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)
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))
910 /* Allocation failure. Weird. */
913 LeaveCriticalSection (&lock->lock);
915 EnterCriticalSection (&lock->lock);
917 while (!(lock->runcount == 0));
920 lock->runcount--; /* runcount becomes -1 */
921 LeaveCriticalSection (&lock->lock);
926 glthread_rwlock_unlock_func (gl_rwlock_t *lock)
928 if (!lock->guard.done)
930 EnterCriticalSection (&lock->lock);
931 if (lock->runcount < 0)
933 /* Drop a writer lock. */
934 if (!(lock->runcount == -1))
940 /* Drop a reader lock. */
941 if (!(lock->runcount > 0))
943 LeaveCriticalSection (&lock->lock);
948 if (lock->runcount == 0)
950 /* POSIX recommends that "write locks shall take precedence over read
951 locks", to avoid "writer starvation". */
952 if (lock->waiting_writers.count > 0)
954 /* Wake up one of the waiting writers. */
956 gl_waitqueue_notify_first (&lock->waiting_writers);
960 /* Wake up all waiting readers. */
961 lock->runcount += lock->waiting_readers.count;
962 gl_waitqueue_notify_all (&lock->waiting_readers);
965 LeaveCriticalSection (&lock->lock);
970 glthread_rwlock_destroy_func (gl_rwlock_t *lock)
972 if (!lock->guard.done)
974 if (lock->runcount != 0)
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;
985 /* --------------------- gl_recursive_lock_t datatype --------------------- */
988 glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
992 InitializeCriticalSection (&lock->lock);
993 lock->guard.done = 1;
997 glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
999 if (!lock->guard.done)
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);
1005 /* Yield the CPU while waiting for another thread to finish
1006 initializing this lock. */
1007 while (!lock->guard.done)
1011 DWORD self = GetCurrentThreadId ();
1012 if (lock->owner != self)
1014 EnterCriticalSection (&lock->lock);
1017 if (++(lock->depth) == 0) /* wraparound? */
1027 glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
1029 if (lock->owner != GetCurrentThreadId ())
1031 if (lock->depth == 0)
1033 if (--(lock->depth) == 0)
1036 LeaveCriticalSection (&lock->lock);
1042 glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
1044 if (lock->owner != 0)
1046 DeleteCriticalSection (&lock->lock);
1047 lock->guard.done = 0;
1051 /* -------------------------- gl_once_t datatype -------------------------- */
1054 glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
1056 if (once_control->inited <= 0)
1058 if (InterlockedIncrement (&once_control->started) == 0)
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;
1065 once_control->inited = 1;
1066 LeaveCriticalSection (&once_control->lock);
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)
1077 if (once_control->inited <= 0)
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))
1092 /* ========================================================================= */