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