1 /* Locking in multithreaded situations.
2 Copyright (C) 2005-2012 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 3, 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, see <http://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
23 #include "glthread/lock.h"
25 /* ========================================================================= */
29 /* -------------------------- gl_lock_t datatype -------------------------- */
31 /* ------------------------- gl_rwlock_t datatype ------------------------- */
33 # if HAVE_PTHREAD_RWLOCK
35 # if !defined PTHREAD_RWLOCK_INITIALIZER
38 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
42 err = pthread_rwlock_init (&lock->rwlock, NULL);
45 lock->initialized = 1;
50 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
52 if (!lock->initialized)
56 err = pthread_mutex_lock (&lock->guard);
59 if (!lock->initialized)
61 err = glthread_rwlock_init_multithreaded (lock);
64 pthread_mutex_unlock (&lock->guard);
68 err = pthread_mutex_unlock (&lock->guard);
72 return pthread_rwlock_rdlock (&lock->rwlock);
76 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
78 if (!lock->initialized)
82 err = pthread_mutex_lock (&lock->guard);
85 if (!lock->initialized)
87 err = glthread_rwlock_init_multithreaded (lock);
90 pthread_mutex_unlock (&lock->guard);
94 err = pthread_mutex_unlock (&lock->guard);
98 return pthread_rwlock_wrlock (&lock->rwlock);
102 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
104 if (!lock->initialized)
106 return pthread_rwlock_unlock (&lock->rwlock);
110 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
114 if (!lock->initialized)
116 err = pthread_rwlock_destroy (&lock->rwlock);
119 lock->initialized = 0;
128 glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
132 err = pthread_mutex_init (&lock->lock, NULL);
135 err = pthread_cond_init (&lock->waiting_readers, NULL);
138 err = pthread_cond_init (&lock->waiting_writers, NULL);
141 lock->waiting_writers_count = 0;
147 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
151 err = pthread_mutex_lock (&lock->lock);
154 /* Test whether only readers are currently running, and whether the runcount
155 field will not overflow. */
156 /* POSIX says: "It is implementation-defined whether the calling thread
157 acquires the lock when a writer does not hold the lock and there are
158 writers blocked on the lock." Let's say, no: give the writers a higher
160 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
162 /* This thread has to wait for a while. Enqueue it among the
164 err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
167 pthread_mutex_unlock (&lock->lock);
172 return pthread_mutex_unlock (&lock->lock);
176 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
180 err = pthread_mutex_lock (&lock->lock);
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 err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
192 lock->waiting_writers_count--;
193 pthread_mutex_unlock (&lock->lock);
196 lock->waiting_writers_count--;
198 lock->runcount--; /* runcount becomes -1 */
199 return pthread_mutex_unlock (&lock->lock);
203 glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
207 err = pthread_mutex_lock (&lock->lock);
210 if (lock->runcount < 0)
212 /* Drop a writer lock. */
213 if (!(lock->runcount == -1))
215 pthread_mutex_unlock (&lock->lock);
222 /* Drop a reader lock. */
223 if (!(lock->runcount > 0))
225 pthread_mutex_unlock (&lock->lock);
230 if (lock->runcount == 0)
232 /* POSIX recommends that "write locks shall take precedence over read
233 locks", to avoid "writer starvation". */
234 if (lock->waiting_writers_count > 0)
236 /* Wake up one of the waiting writers. */
237 err = pthread_cond_signal (&lock->waiting_writers);
240 pthread_mutex_unlock (&lock->lock);
246 /* Wake up all waiting readers. */
247 err = pthread_cond_broadcast (&lock->waiting_readers);
250 pthread_mutex_unlock (&lock->lock);
255 return pthread_mutex_unlock (&lock->lock);
259 glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
263 err = pthread_mutex_destroy (&lock->lock);
266 err = pthread_cond_destroy (&lock->waiting_readers);
269 err = pthread_cond_destroy (&lock->waiting_writers);
277 /* --------------------- gl_recursive_lock_t datatype --------------------- */
279 # if HAVE_PTHREAD_MUTEX_RECURSIVE
281 # if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
284 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
286 pthread_mutexattr_t attributes;
289 err = pthread_mutexattr_init (&attributes);
292 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
295 pthread_mutexattr_destroy (&attributes);
298 err = pthread_mutex_init (lock, &attributes);
301 pthread_mutexattr_destroy (&attributes);
304 err = pthread_mutexattr_destroy (&attributes);
313 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
315 pthread_mutexattr_t attributes;
318 err = pthread_mutexattr_init (&attributes);
321 err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
324 pthread_mutexattr_destroy (&attributes);
327 err = pthread_mutex_init (&lock->recmutex, &attributes);
330 pthread_mutexattr_destroy (&attributes);
333 err = pthread_mutexattr_destroy (&attributes);
336 lock->initialized = 1;
341 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
343 if (!lock->initialized)
347 err = pthread_mutex_lock (&lock->guard);
350 if (!lock->initialized)
352 err = glthread_recursive_lock_init_multithreaded (lock);
355 pthread_mutex_unlock (&lock->guard);
359 err = pthread_mutex_unlock (&lock->guard);
363 return pthread_mutex_lock (&lock->recmutex);
367 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
369 if (!lock->initialized)
371 return pthread_mutex_unlock (&lock->recmutex);
375 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
379 if (!lock->initialized)
381 err = pthread_mutex_destroy (&lock->recmutex);
384 lock->initialized = 0;
393 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
397 err = pthread_mutex_init (&lock->mutex, NULL);
400 lock->owner = (pthread_t) 0;
406 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
408 pthread_t self = pthread_self ();
409 if (lock->owner != self)
413 err = pthread_mutex_lock (&lock->mutex);
418 if (++(lock->depth) == 0) /* wraparound? */
427 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
429 if (lock->owner != pthread_self ())
431 if (lock->depth == 0)
433 if (--(lock->depth) == 0)
435 lock->owner = (pthread_t) 0;
436 return pthread_mutex_unlock (&lock->mutex);
443 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
445 if (lock->owner != (pthread_t) 0)
447 return pthread_mutex_destroy (&lock->mutex);
452 /* -------------------------- gl_once_t datatype -------------------------- */
454 static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
457 glthread_once_singlethreaded (pthread_once_t *once_control)
459 /* We don't know whether pthread_once_t is an integer type, a floating-point
460 type, a pointer type, or a structure type. */
461 char *firstbyte = (char *)once_control;
462 if (*firstbyte == *(const char *)&fresh_once)
464 /* First time use of once_control. Invert the first byte. */
465 *firstbyte = ~ *(const char *)&fresh_once;
474 /* ========================================================================= */
478 /* Use the GNU Pth threads library. */
480 /* -------------------------- gl_lock_t datatype -------------------------- */
482 /* ------------------------- gl_rwlock_t datatype ------------------------- */
484 /* --------------------- gl_recursive_lock_t datatype --------------------- */
486 /* -------------------------- gl_once_t datatype -------------------------- */
489 glthread_once_call (void *arg)
491 void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
492 void (*initfunction) (void) = *gl_once_temp_addr;
497 glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
499 void (*temp) (void) = initfunction;
500 return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
504 glthread_once_singlethreaded (pth_once_t *once_control)
506 /* We know that pth_once_t is an integer type. */
507 if (*once_control == PTH_ONCE_INIT)
509 /* First time use of once_control. Invert the marker. */
510 *once_control = ~ PTH_ONCE_INIT;
519 /* ========================================================================= */
521 #if USE_SOLARIS_THREADS
523 /* Use the old Solaris threads library. */
525 /* -------------------------- gl_lock_t datatype -------------------------- */
527 /* ------------------------- gl_rwlock_t datatype ------------------------- */
529 /* --------------------- gl_recursive_lock_t datatype --------------------- */
532 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
536 err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
539 lock->owner = (thread_t) 0;
545 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
547 thread_t self = thr_self ();
548 if (lock->owner != self)
552 err = mutex_lock (&lock->mutex);
557 if (++(lock->depth) == 0) /* wraparound? */
566 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
568 if (lock->owner != thr_self ())
570 if (lock->depth == 0)
572 if (--(lock->depth) == 0)
574 lock->owner = (thread_t) 0;
575 return mutex_unlock (&lock->mutex);
582 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
584 if (lock->owner != (thread_t) 0)
586 return mutex_destroy (&lock->mutex);
589 /* -------------------------- gl_once_t datatype -------------------------- */
592 glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
594 if (!once_control->inited)
598 /* Use the mutex to guarantee that if another thread is already calling
599 the initfunction, this thread waits until it's finished. */
600 err = mutex_lock (&once_control->mutex);
603 if (!once_control->inited)
605 once_control->inited = 1;
608 return mutex_unlock (&once_control->mutex);
615 glthread_once_singlethreaded (gl_once_t *once_control)
617 /* We know that gl_once_t contains an integer type. */
618 if (!once_control->inited)
620 /* First time use of once_control. Invert the marker. */
621 once_control->inited = ~ 0;
630 /* ========================================================================= */
632 #if USE_WINDOWS_THREADS
634 /* -------------------------- gl_lock_t datatype -------------------------- */
637 glthread_lock_init_func (gl_lock_t *lock)
639 InitializeCriticalSection (&lock->lock);
640 lock->guard.done = 1;
644 glthread_lock_lock_func (gl_lock_t *lock)
646 if (!lock->guard.done)
648 if (InterlockedIncrement (&lock->guard.started) == 0)
649 /* This thread is the first one to need this lock. Initialize it. */
650 glthread_lock_init (lock);
652 /* Yield the CPU while waiting for another thread to finish
653 initializing this lock. */
654 while (!lock->guard.done)
657 EnterCriticalSection (&lock->lock);
662 glthread_lock_unlock_func (gl_lock_t *lock)
664 if (!lock->guard.done)
666 LeaveCriticalSection (&lock->lock);
671 glthread_lock_destroy_func (gl_lock_t *lock)
673 if (!lock->guard.done)
675 DeleteCriticalSection (&lock->lock);
676 lock->guard.done = 0;
680 /* ------------------------- gl_rwlock_t datatype ------------------------- */
682 /* In this file, the waitqueues are implemented as circular arrays. */
683 #define gl_waitqueue_t gl_carray_waitqueue_t
686 gl_waitqueue_init (gl_waitqueue_t *wq)
694 /* Enqueues the current thread, represented by an event, in a wait queue.
695 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
697 gl_waitqueue_add (gl_waitqueue_t *wq)
702 if (wq->count == wq->alloc)
704 unsigned int new_alloc = 2 * wq->alloc + 1;
706 (HANDLE *) realloc (wq->array, new_alloc * sizeof (HANDLE));
707 if (new_array == NULL)
708 /* No more memory. */
709 return INVALID_HANDLE_VALUE;
710 /* Now is a good opportunity to rotate the array so that its contents
711 starts at offset 0. */
714 unsigned int old_count = wq->count;
715 unsigned int old_alloc = wq->alloc;
716 unsigned int old_offset = wq->offset;
718 if (old_offset + old_count > old_alloc)
720 unsigned int limit = old_offset + old_count - old_alloc;
721 for (i = 0; i < limit; i++)
722 new_array[old_alloc + i] = new_array[i];
724 for (i = 0; i < old_count; i++)
725 new_array[i] = new_array[old_offset + i];
728 wq->array = new_array;
729 wq->alloc = new_alloc;
731 /* Whether the created event is a manual-reset one or an auto-reset one,
732 does not matter, since we will wait on it only once. */
733 event = CreateEvent (NULL, TRUE, FALSE, NULL);
734 if (event == INVALID_HANDLE_VALUE)
735 /* No way to allocate an event. */
736 return INVALID_HANDLE_VALUE;
737 index = wq->offset + wq->count;
738 if (index >= wq->alloc)
740 wq->array[index] = event;
745 /* Notifies the first thread from a wait queue and dequeues it. */
747 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
749 SetEvent (wq->array[wq->offset + 0]);
752 if (wq->count == 0 || wq->offset == wq->alloc)
756 /* Notifies all threads from a wait queue and dequeues them all. */
758 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
762 for (i = 0; i < wq->count; i++)
764 unsigned int index = wq->offset + i;
765 if (index >= wq->alloc)
767 SetEvent (wq->array[index]);
774 glthread_rwlock_init_func (gl_rwlock_t *lock)
776 InitializeCriticalSection (&lock->lock);
777 gl_waitqueue_init (&lock->waiting_readers);
778 gl_waitqueue_init (&lock->waiting_writers);
780 lock->guard.done = 1;
784 glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
786 if (!lock->guard.done)
788 if (InterlockedIncrement (&lock->guard.started) == 0)
789 /* This thread is the first one to need this lock. Initialize it. */
790 glthread_rwlock_init (lock);
792 /* Yield the CPU while waiting for another thread to finish
793 initializing this lock. */
794 while (!lock->guard.done)
797 EnterCriticalSection (&lock->lock);
798 /* Test whether only readers are currently running, and whether the runcount
799 field will not overflow. */
800 if (!(lock->runcount + 1 > 0))
802 /* This thread has to wait for a while. Enqueue it among the
804 HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
805 if (event != INVALID_HANDLE_VALUE)
808 LeaveCriticalSection (&lock->lock);
809 /* Wait until another thread signals this event. */
810 result = WaitForSingleObject (event, INFINITE);
811 if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
814 /* The thread which signalled the event already did the bookkeeping:
815 removed us from the waiting_readers, incremented lock->runcount. */
816 if (!(lock->runcount > 0))
822 /* Allocation failure. Weird. */
825 LeaveCriticalSection (&lock->lock);
827 EnterCriticalSection (&lock->lock);
829 while (!(lock->runcount + 1 > 0));
833 LeaveCriticalSection (&lock->lock);
838 glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
840 if (!lock->guard.done)
842 if (InterlockedIncrement (&lock->guard.started) == 0)
843 /* This thread is the first one to need this lock. Initialize it. */
844 glthread_rwlock_init (lock);
846 /* Yield the CPU while waiting for another thread to finish
847 initializing this lock. */
848 while (!lock->guard.done)
851 EnterCriticalSection (&lock->lock);
852 /* Test whether no readers or writers are currently running. */
853 if (!(lock->runcount == 0))
855 /* This thread has to wait for a while. Enqueue it among the
857 HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
858 if (event != INVALID_HANDLE_VALUE)
861 LeaveCriticalSection (&lock->lock);
862 /* Wait until another thread signals this event. */
863 result = WaitForSingleObject (event, INFINITE);
864 if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
867 /* The thread which signalled the event already did the bookkeeping:
868 removed us from the waiting_writers, set lock->runcount = -1. */
869 if (!(lock->runcount == -1))
875 /* Allocation failure. Weird. */
878 LeaveCriticalSection (&lock->lock);
880 EnterCriticalSection (&lock->lock);
882 while (!(lock->runcount == 0));
885 lock->runcount--; /* runcount becomes -1 */
886 LeaveCriticalSection (&lock->lock);
891 glthread_rwlock_unlock_func (gl_rwlock_t *lock)
893 if (!lock->guard.done)
895 EnterCriticalSection (&lock->lock);
896 if (lock->runcount < 0)
898 /* Drop a writer lock. */
899 if (!(lock->runcount == -1))
905 /* Drop a reader lock. */
906 if (!(lock->runcount > 0))
908 LeaveCriticalSection (&lock->lock);
913 if (lock->runcount == 0)
915 /* POSIX recommends that "write locks shall take precedence over read
916 locks", to avoid "writer starvation". */
917 if (lock->waiting_writers.count > 0)
919 /* Wake up one of the waiting writers. */
921 gl_waitqueue_notify_first (&lock->waiting_writers);
925 /* Wake up all waiting readers. */
926 lock->runcount += lock->waiting_readers.count;
927 gl_waitqueue_notify_all (&lock->waiting_readers);
930 LeaveCriticalSection (&lock->lock);
935 glthread_rwlock_destroy_func (gl_rwlock_t *lock)
937 if (!lock->guard.done)
939 if (lock->runcount != 0)
941 DeleteCriticalSection (&lock->lock);
942 if (lock->waiting_readers.array != NULL)
943 free (lock->waiting_readers.array);
944 if (lock->waiting_writers.array != NULL)
945 free (lock->waiting_writers.array);
946 lock->guard.done = 0;
950 /* --------------------- gl_recursive_lock_t datatype --------------------- */
953 glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
957 InitializeCriticalSection (&lock->lock);
958 lock->guard.done = 1;
962 glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
964 if (!lock->guard.done)
966 if (InterlockedIncrement (&lock->guard.started) == 0)
967 /* This thread is the first one to need this lock. Initialize it. */
968 glthread_recursive_lock_init (lock);
970 /* Yield the CPU while waiting for another thread to finish
971 initializing this lock. */
972 while (!lock->guard.done)
976 DWORD self = GetCurrentThreadId ();
977 if (lock->owner != self)
979 EnterCriticalSection (&lock->lock);
982 if (++(lock->depth) == 0) /* wraparound? */
992 glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
994 if (lock->owner != GetCurrentThreadId ())
996 if (lock->depth == 0)
998 if (--(lock->depth) == 0)
1001 LeaveCriticalSection (&lock->lock);
1007 glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
1009 if (lock->owner != 0)
1011 DeleteCriticalSection (&lock->lock);
1012 lock->guard.done = 0;
1016 /* -------------------------- gl_once_t datatype -------------------------- */
1019 glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
1021 if (once_control->inited <= 0)
1023 if (InterlockedIncrement (&once_control->started) == 0)
1025 /* This thread is the first one to come to this once_control. */
1026 InitializeCriticalSection (&once_control->lock);
1027 EnterCriticalSection (&once_control->lock);
1028 once_control->inited = 0;
1030 once_control->inited = 1;
1031 LeaveCriticalSection (&once_control->lock);
1035 /* Undo last operation. */
1036 InterlockedDecrement (&once_control->started);
1037 /* Some other thread has already started the initialization.
1038 Yield the CPU while waiting for the other thread to finish
1039 initializing and taking the lock. */
1040 while (once_control->inited < 0)
1042 if (once_control->inited <= 0)
1044 /* Take the lock. This blocks until the other thread has
1045 finished calling the initfunction. */
1046 EnterCriticalSection (&once_control->lock);
1047 LeaveCriticalSection (&once_control->lock);
1048 if (!(once_control->inited > 0))
1057 /* ========================================================================= */