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
/* ========================================================================= */