1 /* Locking in multithreaded situations.
2 Copyright (C) 2005-2020 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 <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 Based on GCC's gthr-posix.h, gthr-posix95.h. */
22 #include "glthread/lock.h"
24 /* ========================================================================= */
26 #if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
28 /* -------------------------- gl_lock_t datatype -------------------------- */
31 glthread_lock_init (gl_lock_t
*lock
)
33 if (mtx_init (&lock
->mutex
, mtx_plain
) != thrd_success
)
35 lock
->init_needed
= 0;
40 glthread_lock_lock (gl_lock_t
*lock
)
42 if (lock
->init_needed
)
43 call_once (&lock
->init_once
, lock
->init_func
);
44 if (mtx_lock (&lock
->mutex
) != thrd_success
)
50 glthread_lock_unlock (gl_lock_t
*lock
)
52 if (lock
->init_needed
)
53 call_once (&lock
->init_once
, lock
->init_func
);
54 if (mtx_unlock (&lock
->mutex
) != thrd_success
)
60 glthread_lock_destroy (gl_lock_t
*lock
)
62 if (lock
->init_needed
)
63 call_once (&lock
->init_once
, lock
->init_func
);
64 mtx_destroy (&lock
->mutex
);
68 /* ------------------------- gl_rwlock_t datatype ------------------------- */
71 glthread_rwlock_init (gl_rwlock_t
*lock
)
73 if (mtx_init (&lock
->lock
, mtx_plain
) != thrd_success
74 || cnd_init (&lock
->waiting_readers
) != thrd_success
75 || cnd_init (&lock
->waiting_writers
) != thrd_success
)
77 lock
->waiting_writers_count
= 0;
79 lock
->init_needed
= 0;
84 glthread_rwlock_rdlock (gl_rwlock_t
*lock
)
86 if (lock
->init_needed
)
87 call_once (&lock
->init_once
, lock
->init_func
);
88 if (mtx_lock (&lock
->lock
) != thrd_success
)
90 /* Test whether only readers are currently running, and whether the runcount
91 field will not overflow, and whether no writer is waiting. The latter
92 condition is because POSIX recommends that "write locks shall take
93 precedence over read locks", to avoid "writer starvation". */
94 while (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers_count
== 0))
96 /* This thread has to wait for a while. Enqueue it among the
98 if (cnd_wait (&lock
->waiting_readers
, &lock
->lock
) != thrd_success
)
100 mtx_unlock (&lock
->lock
);
105 if (mtx_unlock (&lock
->lock
) != thrd_success
)
111 glthread_rwlock_wrlock (gl_rwlock_t
*lock
)
113 if (lock
->init_needed
)
114 call_once (&lock
->init_once
, lock
->init_func
);
115 if (mtx_lock (&lock
->lock
) != thrd_success
)
117 /* Test whether no readers or writers are currently running. */
118 while (!(lock
->runcount
== 0))
120 /* This thread has to wait for a while. Enqueue it among the
122 lock
->waiting_writers_count
++;
123 if (cnd_wait (&lock
->waiting_writers
, &lock
->lock
) != thrd_success
)
125 lock
->waiting_writers_count
--;
126 mtx_unlock (&lock
->lock
);
129 lock
->waiting_writers_count
--;
131 lock
->runcount
--; /* runcount becomes -1 */
132 if (mtx_unlock (&lock
->lock
) != thrd_success
)
138 glthread_rwlock_unlock (gl_rwlock_t
*lock
)
140 if (lock
->init_needed
)
141 call_once (&lock
->init_once
, lock
->init_func
);
142 if (mtx_lock (&lock
->lock
) != thrd_success
)
144 if (lock
->runcount
< 0)
146 /* Drop a writer lock. */
147 if (!(lock
->runcount
== -1))
149 mtx_unlock (&lock
->lock
);
156 /* Drop a reader lock. */
157 if (!(lock
->runcount
> 0))
159 mtx_unlock (&lock
->lock
);
164 if (lock
->runcount
== 0)
166 /* POSIX recommends that "write locks shall take precedence over read
167 locks", to avoid "writer starvation". */
168 if (lock
->waiting_writers_count
> 0)
170 /* Wake up one of the waiting writers. */
171 if (cnd_signal (&lock
->waiting_writers
) != thrd_success
)
173 mtx_unlock (&lock
->lock
);
179 /* Wake up all waiting readers. */
180 if (cnd_broadcast (&lock
->waiting_readers
) != thrd_success
)
182 mtx_unlock (&lock
->lock
);
187 if (mtx_unlock (&lock
->lock
) != thrd_success
)
193 glthread_rwlock_destroy (gl_rwlock_t
*lock
)
195 if (lock
->init_needed
)
196 call_once (&lock
->init_once
, lock
->init_func
);
197 mtx_destroy (&lock
->lock
);
198 cnd_destroy (&lock
->waiting_readers
);
199 cnd_destroy (&lock
->waiting_writers
);
203 /* --------------------- gl_recursive_lock_t datatype --------------------- */
206 glthread_recursive_lock_init (gl_recursive_lock_t
*lock
)
208 if (mtx_init (&lock
->mutex
, mtx_plain
| mtx_recursive
) != thrd_success
)
210 lock
->init_needed
= 0;
215 glthread_recursive_lock_lock (gl_recursive_lock_t
*lock
)
217 if (lock
->init_needed
)
218 call_once (&lock
->init_once
, lock
->init_func
);
219 if (mtx_lock (&lock
->mutex
) != thrd_success
)
225 glthread_recursive_lock_unlock (gl_recursive_lock_t
*lock
)
227 if (lock
->init_needed
)
228 call_once (&lock
->init_once
, lock
->init_func
);
229 if (mtx_unlock (&lock
->mutex
) != thrd_success
)
235 glthread_recursive_lock_destroy (gl_recursive_lock_t
*lock
)
237 if (lock
->init_needed
)
238 call_once (&lock
->init_once
, lock
->init_func
);
239 mtx_destroy (&lock
->mutex
);
243 /* -------------------------- gl_once_t datatype -------------------------- */
247 /* ========================================================================= */
249 #if USE_POSIX_THREADS
251 /* -------------------------- gl_lock_t datatype -------------------------- */
253 /* ------------------------- gl_rwlock_t datatype ------------------------- */
255 # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
257 # if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
259 # if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
260 /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
263 glthread_rwlock_init_for_glibc (pthread_rwlock_t
*lock
)
265 pthread_rwlockattr_t attributes
;
268 err
= pthread_rwlockattr_init (&attributes
);
271 /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
272 causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
274 http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
275 err
= pthread_rwlockattr_setkind_np (&attributes
,
276 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
);
278 err
= pthread_rwlock_init(lock
, &attributes
);
279 /* pthread_rwlockattr_destroy always returns 0. It cannot influence the
281 pthread_rwlockattr_destroy (&attributes
);
289 glthread_rwlock_init_multithreaded (gl_rwlock_t
*lock
)
293 err
= pthread_rwlock_init (&lock
->rwlock
, NULL
);
296 lock
->initialized
= 1;
301 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t
*lock
)
303 if (!lock
->initialized
)
307 err
= pthread_mutex_lock (&lock
->guard
);
310 if (!lock
->initialized
)
312 err
= glthread_rwlock_init_multithreaded (lock
);
315 pthread_mutex_unlock (&lock
->guard
);
319 err
= pthread_mutex_unlock (&lock
->guard
);
323 return pthread_rwlock_rdlock (&lock
->rwlock
);
327 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t
*lock
)
329 if (!lock
->initialized
)
333 err
= pthread_mutex_lock (&lock
->guard
);
336 if (!lock
->initialized
)
338 err
= glthread_rwlock_init_multithreaded (lock
);
341 pthread_mutex_unlock (&lock
->guard
);
345 err
= pthread_mutex_unlock (&lock
->guard
);
349 return pthread_rwlock_wrlock (&lock
->rwlock
);
353 glthread_rwlock_unlock_multithreaded (gl_rwlock_t
*lock
)
355 if (!lock
->initialized
)
357 return pthread_rwlock_unlock (&lock
->rwlock
);
361 glthread_rwlock_destroy_multithreaded (gl_rwlock_t
*lock
)
365 if (!lock
->initialized
)
367 err
= pthread_rwlock_destroy (&lock
->rwlock
);
370 lock
->initialized
= 0;
379 glthread_rwlock_init_multithreaded (gl_rwlock_t
*lock
)
383 err
= pthread_mutex_init (&lock
->lock
, NULL
);
386 err
= pthread_cond_init (&lock
->waiting_readers
, NULL
);
389 err
= pthread_cond_init (&lock
->waiting_writers
, NULL
);
392 lock
->waiting_writers_count
= 0;
398 glthread_rwlock_rdlock_multithreaded (gl_rwlock_t
*lock
)
402 err
= pthread_mutex_lock (&lock
->lock
);
405 /* Test whether only readers are currently running, and whether the runcount
406 field will not overflow, and whether no writer is waiting. The latter
407 condition is because POSIX recommends that "write locks shall take
408 precedence over read locks", to avoid "writer starvation". */
409 while (!(lock
->runcount
+ 1 > 0 && lock
->waiting_writers_count
== 0))
411 /* This thread has to wait for a while. Enqueue it among the
413 err
= pthread_cond_wait (&lock
->waiting_readers
, &lock
->lock
);
416 pthread_mutex_unlock (&lock
->lock
);
421 return pthread_mutex_unlock (&lock
->lock
);
425 glthread_rwlock_wrlock_multithreaded (gl_rwlock_t
*lock
)
429 err
= pthread_mutex_lock (&lock
->lock
);
432 /* Test whether no readers or writers are currently running. */
433 while (!(lock
->runcount
== 0))
435 /* This thread has to wait for a while. Enqueue it among the
437 lock
->waiting_writers_count
++;
438 err
= pthread_cond_wait (&lock
->waiting_writers
, &lock
->lock
);
441 lock
->waiting_writers_count
--;
442 pthread_mutex_unlock (&lock
->lock
);
445 lock
->waiting_writers_count
--;
447 lock
->runcount
--; /* runcount becomes -1 */
448 return pthread_mutex_unlock (&lock
->lock
);
452 glthread_rwlock_unlock_multithreaded (gl_rwlock_t
*lock
)
456 err
= pthread_mutex_lock (&lock
->lock
);
459 if (lock
->runcount
< 0)
461 /* Drop a writer lock. */
462 if (!(lock
->runcount
== -1))
464 pthread_mutex_unlock (&lock
->lock
);
471 /* Drop a reader lock. */
472 if (!(lock
->runcount
> 0))
474 pthread_mutex_unlock (&lock
->lock
);
479 if (lock
->runcount
== 0)
481 /* POSIX recommends that "write locks shall take precedence over read
482 locks", to avoid "writer starvation". */
483 if (lock
->waiting_writers_count
> 0)
485 /* Wake up one of the waiting writers. */
486 err
= pthread_cond_signal (&lock
->waiting_writers
);
489 pthread_mutex_unlock (&lock
->lock
);
495 /* Wake up all waiting readers. */
496 err
= pthread_cond_broadcast (&lock
->waiting_readers
);
499 pthread_mutex_unlock (&lock
->lock
);
504 return pthread_mutex_unlock (&lock
->lock
);
508 glthread_rwlock_destroy_multithreaded (gl_rwlock_t
*lock
)
512 err
= pthread_mutex_destroy (&lock
->lock
);
515 err
= pthread_cond_destroy (&lock
->waiting_readers
);
518 err
= pthread_cond_destroy (&lock
->waiting_writers
);
526 /* --------------------- gl_recursive_lock_t datatype --------------------- */
528 # if HAVE_PTHREAD_MUTEX_RECURSIVE
530 # if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
533 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
535 pthread_mutexattr_t attributes
;
538 err
= pthread_mutexattr_init (&attributes
);
541 err
= pthread_mutexattr_settype (&attributes
, PTHREAD_MUTEX_RECURSIVE
);
544 pthread_mutexattr_destroy (&attributes
);
547 err
= pthread_mutex_init (lock
, &attributes
);
550 pthread_mutexattr_destroy (&attributes
);
553 err
= pthread_mutexattr_destroy (&attributes
);
562 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
564 pthread_mutexattr_t attributes
;
567 err
= pthread_mutexattr_init (&attributes
);
570 err
= pthread_mutexattr_settype (&attributes
, PTHREAD_MUTEX_RECURSIVE
);
573 pthread_mutexattr_destroy (&attributes
);
576 err
= pthread_mutex_init (&lock
->recmutex
, &attributes
);
579 pthread_mutexattr_destroy (&attributes
);
582 err
= pthread_mutexattr_destroy (&attributes
);
585 lock
->initialized
= 1;
590 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t
*lock
)
592 if (!lock
->initialized
)
596 err
= pthread_mutex_lock (&lock
->guard
);
599 if (!lock
->initialized
)
601 err
= glthread_recursive_lock_init_multithreaded (lock
);
604 pthread_mutex_unlock (&lock
->guard
);
608 err
= pthread_mutex_unlock (&lock
->guard
);
612 return pthread_mutex_lock (&lock
->recmutex
);
616 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t
*lock
)
618 if (!lock
->initialized
)
620 return pthread_mutex_unlock (&lock
->recmutex
);
624 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t
*lock
)
628 if (!lock
->initialized
)
630 err
= pthread_mutex_destroy (&lock
->recmutex
);
633 lock
->initialized
= 0;
642 glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t
*lock
)
646 err
= pthread_mutex_init (&lock
->mutex
, NULL
);
649 lock
->owner
= (pthread_t
) 0;
655 glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t
*lock
)
657 pthread_t self
= pthread_self ();
658 if (lock
->owner
!= self
)
662 err
= pthread_mutex_lock (&lock
->mutex
);
667 if (++(lock
->depth
) == 0) /* wraparound? */
676 glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t
*lock
)
678 if (lock
->owner
!= pthread_self ())
680 if (lock
->depth
== 0)
682 if (--(lock
->depth
) == 0)
684 lock
->owner
= (pthread_t
) 0;
685 return pthread_mutex_unlock (&lock
->mutex
);
692 glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t
*lock
)
694 if (lock
->owner
!= (pthread_t
) 0)
696 return pthread_mutex_destroy (&lock
->mutex
);
701 /* -------------------------- gl_once_t datatype -------------------------- */
703 static const pthread_once_t fresh_once
= PTHREAD_ONCE_INIT
;
706 glthread_once_singlethreaded (pthread_once_t
*once_control
)
708 /* We don't know whether pthread_once_t is an integer type, a floating-point
709 type, a pointer type, or a structure type. */
710 char *firstbyte
= (char *)once_control
;
711 if (*firstbyte
== *(const char *)&fresh_once
)
713 /* First time use of once_control. Invert the first byte. */
714 *firstbyte
= ~ *(const char *)&fresh_once
;
721 # if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
724 glthread_once_multithreaded (pthread_once_t
*once_control
,
725 void (*init_function
) (void))
727 int err
= pthread_once (once_control
, init_function
);
730 /* This happens on FreeBSD 11: The pthread_once function in libc returns
732 if (glthread_once_singlethreaded (once_control
))
743 /* ========================================================================= */
745 #if USE_WINDOWS_THREADS
749 /* ========================================================================= */