Line data Source code
1 : /* Locking in multithreaded situations.
2 : Copyright (C) 2005-2020 Free Software Foundation, Inc.
3 :
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)
7 : any later version.
8 :
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.
13 :
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/>. */
16 :
17 : /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 : Based on GCC's gthr-posix.h, gthr-posix95.h. */
19 :
20 : #include <config.h>
21 :
22 : #include "glthread/lock.h"
23 :
24 : /* ========================================================================= */
25 :
26 : #if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
27 :
28 : /* -------------------------- gl_lock_t datatype -------------------------- */
29 :
30 : int
31 : glthread_lock_init (gl_lock_t *lock)
32 : {
33 : if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
34 : return ENOMEM;
35 : lock->init_needed = 0;
36 : return 0;
37 : }
38 :
39 : int
40 : glthread_lock_lock (gl_lock_t *lock)
41 : {
42 : if (lock->init_needed)
43 : call_once (&lock->init_once, lock->init_func);
44 : if (mtx_lock (&lock->mutex) != thrd_success)
45 : return EAGAIN;
46 : return 0;
47 : }
48 :
49 : int
50 : glthread_lock_unlock (gl_lock_t *lock)
51 : {
52 : if (lock->init_needed)
53 : call_once (&lock->init_once, lock->init_func);
54 : if (mtx_unlock (&lock->mutex) != thrd_success)
55 : return EINVAL;
56 : return 0;
57 : }
58 :
59 : int
60 : glthread_lock_destroy (gl_lock_t *lock)
61 : {
62 : if (lock->init_needed)
63 : call_once (&lock->init_once, lock->init_func);
64 : mtx_destroy (&lock->mutex);
65 : return 0;
66 : }
67 :
68 : /* ------------------------- gl_rwlock_t datatype ------------------------- */
69 :
70 : int
71 : glthread_rwlock_init (gl_rwlock_t *lock)
72 : {
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)
76 : return ENOMEM;
77 : lock->waiting_writers_count = 0;
78 : lock->runcount = 0;
79 : lock->init_needed = 0;
80 : return 0;
81 : }
82 :
83 : int
84 : glthread_rwlock_rdlock (gl_rwlock_t *lock)
85 : {
86 : if (lock->init_needed)
87 : call_once (&lock->init_once, lock->init_func);
88 : if (mtx_lock (&lock->lock) != thrd_success)
89 : return EAGAIN;
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))
95 : {
96 : /* This thread has to wait for a while. Enqueue it among the
97 : waiting_readers. */
98 : if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
99 : {
100 : mtx_unlock (&lock->lock);
101 : return EINVAL;
102 : }
103 : }
104 : lock->runcount++;
105 : if (mtx_unlock (&lock->lock) != thrd_success)
106 : return EINVAL;
107 : return 0;
108 : }
109 :
110 : int
111 : glthread_rwlock_wrlock (gl_rwlock_t *lock)
112 : {
113 : if (lock->init_needed)
114 : call_once (&lock->init_once, lock->init_func);
115 : if (mtx_lock (&lock->lock) != thrd_success)
116 : return EAGAIN;
117 : /* Test whether no readers or writers are currently running. */
118 : while (!(lock->runcount == 0))
119 : {
120 : /* This thread has to wait for a while. Enqueue it among the
121 : waiting_writers. */
122 : lock->waiting_writers_count++;
123 : if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
124 : {
125 : lock->waiting_writers_count--;
126 : mtx_unlock (&lock->lock);
127 : return EINVAL;
128 : }
129 : lock->waiting_writers_count--;
130 : }
131 : lock->runcount--; /* runcount becomes -1 */
132 : if (mtx_unlock (&lock->lock) != thrd_success)
133 : return EINVAL;
134 : return 0;
135 : }
136 :
137 : int
138 : glthread_rwlock_unlock (gl_rwlock_t *lock)
139 : {
140 : if (lock->init_needed)
141 : call_once (&lock->init_once, lock->init_func);
142 : if (mtx_lock (&lock->lock) != thrd_success)
143 : return EAGAIN;
144 : if (lock->runcount < 0)
145 : {
146 : /* Drop a writer lock. */
147 : if (!(lock->runcount == -1))
148 : {
149 : mtx_unlock (&lock->lock);
150 : return EINVAL;
151 : }
152 : lock->runcount = 0;
153 : }
154 : else
155 : {
156 : /* Drop a reader lock. */
157 : if (!(lock->runcount > 0))
158 : {
159 : mtx_unlock (&lock->lock);
160 : return EINVAL;
161 : }
162 : lock->runcount--;
163 : }
164 : if (lock->runcount == 0)
165 : {
166 : /* POSIX recommends that "write locks shall take precedence over read
167 : locks", to avoid "writer starvation". */
168 : if (lock->waiting_writers_count > 0)
169 : {
170 : /* Wake up one of the waiting writers. */
171 : if (cnd_signal (&lock->waiting_writers) != thrd_success)
172 : {
173 : mtx_unlock (&lock->lock);
174 : return EINVAL;
175 : }
176 : }
177 : else
178 : {
179 : /* Wake up all waiting readers. */
180 : if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
181 : {
182 : mtx_unlock (&lock->lock);
183 : return EINVAL;
184 : }
185 : }
186 : }
187 : if (mtx_unlock (&lock->lock) != thrd_success)
188 : return EINVAL;
189 : return 0;
190 : }
191 :
192 : int
193 : glthread_rwlock_destroy (gl_rwlock_t *lock)
194 : {
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);
200 : return 0;
201 : }
202 :
203 : /* --------------------- gl_recursive_lock_t datatype --------------------- */
204 :
205 : int
206 : glthread_recursive_lock_init (gl_recursive_lock_t *lock)
207 : {
208 : if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
209 : return ENOMEM;
210 : lock->init_needed = 0;
211 : return 0;
212 : }
213 :
214 : int
215 : glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
216 : {
217 : if (lock->init_needed)
218 : call_once (&lock->init_once, lock->init_func);
219 : if (mtx_lock (&lock->mutex) != thrd_success)
220 : return EAGAIN;
221 : return 0;
222 : }
223 :
224 : int
225 : glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
226 : {
227 : if (lock->init_needed)
228 : call_once (&lock->init_once, lock->init_func);
229 : if (mtx_unlock (&lock->mutex) != thrd_success)
230 : return EINVAL;
231 : return 0;
232 : }
233 :
234 : int
235 : glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
236 : {
237 : if (lock->init_needed)
238 : call_once (&lock->init_once, lock->init_func);
239 : mtx_destroy (&lock->mutex);
240 : return 0;
241 : }
242 :
243 : /* -------------------------- gl_once_t datatype -------------------------- */
244 :
245 : #endif
246 :
247 : /* ========================================================================= */
248 :
249 : #if USE_POSIX_THREADS
250 :
251 : /* -------------------------- gl_lock_t datatype -------------------------- */
252 :
253 : /* ------------------------- gl_rwlock_t datatype ------------------------- */
254 :
255 : # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
256 :
257 : # if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
258 :
259 : # if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
260 : /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
261 :
262 : int
263 50001 : glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
264 : {
265 : pthread_rwlockattr_t attributes;
266 : int err;
267 :
268 50001 : err = pthread_rwlockattr_init (&attributes);
269 50001 : if (err != 0)
270 0 : return err;
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
273 : do this; see
274 : http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
275 50001 : err = pthread_rwlockattr_setkind_np (&attributes,
276 : PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
277 50001 : if (err == 0)
278 50001 : err = pthread_rwlock_init(lock, &attributes);
279 : /* pthread_rwlockattr_destroy always returns 0. It cannot influence the
280 : return value. */
281 50001 : pthread_rwlockattr_destroy (&attributes);
282 50001 : return err;
283 : }
284 :
285 : # endif
286 : # else
287 :
288 : int
289 : glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
290 : {
291 : int err;
292 :
293 : err = pthread_rwlock_init (&lock->rwlock, NULL);
294 : if (err != 0)
295 : return err;
296 : lock->initialized = 1;
297 : return 0;
298 : }
299 :
300 : int
301 : glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
302 : {
303 : if (!lock->initialized)
304 : {
305 : int err;
306 :
307 : err = pthread_mutex_lock (&lock->guard);
308 : if (err != 0)
309 : return err;
310 : if (!lock->initialized)
311 : {
312 : err = glthread_rwlock_init_multithreaded (lock);
313 : if (err != 0)
314 : {
315 : pthread_mutex_unlock (&lock->guard);
316 : return err;
317 : }
318 : }
319 : err = pthread_mutex_unlock (&lock->guard);
320 : if (err != 0)
321 : return err;
322 : }
323 : return pthread_rwlock_rdlock (&lock->rwlock);
324 : }
325 :
326 : int
327 : glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
328 : {
329 : if (!lock->initialized)
330 : {
331 : int err;
332 :
333 : err = pthread_mutex_lock (&lock->guard);
334 : if (err != 0)
335 : return err;
336 : if (!lock->initialized)
337 : {
338 : err = glthread_rwlock_init_multithreaded (lock);
339 : if (err != 0)
340 : {
341 : pthread_mutex_unlock (&lock->guard);
342 : return err;
343 : }
344 : }
345 : err = pthread_mutex_unlock (&lock->guard);
346 : if (err != 0)
347 : return err;
348 : }
349 : return pthread_rwlock_wrlock (&lock->rwlock);
350 : }
351 :
352 : int
353 : glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
354 : {
355 : if (!lock->initialized)
356 : return EINVAL;
357 : return pthread_rwlock_unlock (&lock->rwlock);
358 : }
359 :
360 : int
361 : glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
362 : {
363 : int err;
364 :
365 : if (!lock->initialized)
366 : return EINVAL;
367 : err = pthread_rwlock_destroy (&lock->rwlock);
368 : if (err != 0)
369 : return err;
370 : lock->initialized = 0;
371 : return 0;
372 : }
373 :
374 : # endif
375 :
376 : # else
377 :
378 : int
379 : glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
380 : {
381 : int err;
382 :
383 : err = pthread_mutex_init (&lock->lock, NULL);
384 : if (err != 0)
385 : return err;
386 : err = pthread_cond_init (&lock->waiting_readers, NULL);
387 : if (err != 0)
388 : return err;
389 : err = pthread_cond_init (&lock->waiting_writers, NULL);
390 : if (err != 0)
391 : return err;
392 : lock->waiting_writers_count = 0;
393 : lock->runcount = 0;
394 : return 0;
395 : }
396 :
397 : int
398 : glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
399 : {
400 : int err;
401 :
402 : err = pthread_mutex_lock (&lock->lock);
403 : if (err != 0)
404 : return err;
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))
410 : {
411 : /* This thread has to wait for a while. Enqueue it among the
412 : waiting_readers. */
413 : err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
414 : if (err != 0)
415 : {
416 : pthread_mutex_unlock (&lock->lock);
417 : return err;
418 : }
419 : }
420 : lock->runcount++;
421 : return pthread_mutex_unlock (&lock->lock);
422 : }
423 :
424 : int
425 : glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
426 : {
427 : int err;
428 :
429 : err = pthread_mutex_lock (&lock->lock);
430 : if (err != 0)
431 : return err;
432 : /* Test whether no readers or writers are currently running. */
433 : while (!(lock->runcount == 0))
434 : {
435 : /* This thread has to wait for a while. Enqueue it among the
436 : waiting_writers. */
437 : lock->waiting_writers_count++;
438 : err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
439 : if (err != 0)
440 : {
441 : lock->waiting_writers_count--;
442 : pthread_mutex_unlock (&lock->lock);
443 : return err;
444 : }
445 : lock->waiting_writers_count--;
446 : }
447 : lock->runcount--; /* runcount becomes -1 */
448 : return pthread_mutex_unlock (&lock->lock);
449 : }
450 :
451 : int
452 : glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
453 : {
454 : int err;
455 :
456 : err = pthread_mutex_lock (&lock->lock);
457 : if (err != 0)
458 : return err;
459 : if (lock->runcount < 0)
460 : {
461 : /* Drop a writer lock. */
462 : if (!(lock->runcount == -1))
463 : {
464 : pthread_mutex_unlock (&lock->lock);
465 : return EINVAL;
466 : }
467 : lock->runcount = 0;
468 : }
469 : else
470 : {
471 : /* Drop a reader lock. */
472 : if (!(lock->runcount > 0))
473 : {
474 : pthread_mutex_unlock (&lock->lock);
475 : return EINVAL;
476 : }
477 : lock->runcount--;
478 : }
479 : if (lock->runcount == 0)
480 : {
481 : /* POSIX recommends that "write locks shall take precedence over read
482 : locks", to avoid "writer starvation". */
483 : if (lock->waiting_writers_count > 0)
484 : {
485 : /* Wake up one of the waiting writers. */
486 : err = pthread_cond_signal (&lock->waiting_writers);
487 : if (err != 0)
488 : {
489 : pthread_mutex_unlock (&lock->lock);
490 : return err;
491 : }
492 : }
493 : else
494 : {
495 : /* Wake up all waiting readers. */
496 : err = pthread_cond_broadcast (&lock->waiting_readers);
497 : if (err != 0)
498 : {
499 : pthread_mutex_unlock (&lock->lock);
500 : return err;
501 : }
502 : }
503 : }
504 : return pthread_mutex_unlock (&lock->lock);
505 : }
506 :
507 : int
508 : glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
509 : {
510 : int err;
511 :
512 : err = pthread_mutex_destroy (&lock->lock);
513 : if (err != 0)
514 : return err;
515 : err = pthread_cond_destroy (&lock->waiting_readers);
516 : if (err != 0)
517 : return err;
518 : err = pthread_cond_destroy (&lock->waiting_writers);
519 : if (err != 0)
520 : return err;
521 : return 0;
522 : }
523 :
524 : # endif
525 :
526 : /* --------------------- gl_recursive_lock_t datatype --------------------- */
527 :
528 : # if HAVE_PTHREAD_MUTEX_RECURSIVE
529 :
530 : # if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
531 :
532 : int
533 0 : glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
534 : {
535 : pthread_mutexattr_t attributes;
536 : int err;
537 :
538 0 : err = pthread_mutexattr_init (&attributes);
539 0 : if (err != 0)
540 0 : return err;
541 0 : err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
542 0 : if (err != 0)
543 : {
544 0 : pthread_mutexattr_destroy (&attributes);
545 0 : return err;
546 : }
547 0 : err = pthread_mutex_init (lock, &attributes);
548 0 : if (err != 0)
549 : {
550 0 : pthread_mutexattr_destroy (&attributes);
551 0 : return err;
552 : }
553 0 : err = pthread_mutexattr_destroy (&attributes);
554 0 : if (err != 0)
555 0 : return err;
556 0 : return 0;
557 : }
558 :
559 : # else
560 :
561 : int
562 : glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
563 : {
564 : pthread_mutexattr_t attributes;
565 : int err;
566 :
567 : err = pthread_mutexattr_init (&attributes);
568 : if (err != 0)
569 : return err;
570 : err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
571 : if (err != 0)
572 : {
573 : pthread_mutexattr_destroy (&attributes);
574 : return err;
575 : }
576 : err = pthread_mutex_init (&lock->recmutex, &attributes);
577 : if (err != 0)
578 : {
579 : pthread_mutexattr_destroy (&attributes);
580 : return err;
581 : }
582 : err = pthread_mutexattr_destroy (&attributes);
583 : if (err != 0)
584 : return err;
585 : lock->initialized = 1;
586 : return 0;
587 : }
588 :
589 : int
590 : glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
591 : {
592 : if (!lock->initialized)
593 : {
594 : int err;
595 :
596 : err = pthread_mutex_lock (&lock->guard);
597 : if (err != 0)
598 : return err;
599 : if (!lock->initialized)
600 : {
601 : err = glthread_recursive_lock_init_multithreaded (lock);
602 : if (err != 0)
603 : {
604 : pthread_mutex_unlock (&lock->guard);
605 : return err;
606 : }
607 : }
608 : err = pthread_mutex_unlock (&lock->guard);
609 : if (err != 0)
610 : return err;
611 : }
612 : return pthread_mutex_lock (&lock->recmutex);
613 : }
614 :
615 : int
616 : glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
617 : {
618 : if (!lock->initialized)
619 : return EINVAL;
620 : return pthread_mutex_unlock (&lock->recmutex);
621 : }
622 :
623 : int
624 : glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
625 : {
626 : int err;
627 :
628 : if (!lock->initialized)
629 : return EINVAL;
630 : err = pthread_mutex_destroy (&lock->recmutex);
631 : if (err != 0)
632 : return err;
633 : lock->initialized = 0;
634 : return 0;
635 : }
636 :
637 : # endif
638 :
639 : # else
640 :
641 : int
642 : glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
643 : {
644 : int err;
645 :
646 : err = pthread_mutex_init (&lock->mutex, NULL);
647 : if (err != 0)
648 : return err;
649 : lock->owner = (pthread_t) 0;
650 : lock->depth = 0;
651 : return 0;
652 : }
653 :
654 : int
655 : glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
656 : {
657 : pthread_t self = pthread_self ();
658 : if (lock->owner != self)
659 : {
660 : int err;
661 :
662 : err = pthread_mutex_lock (&lock->mutex);
663 : if (err != 0)
664 : return err;
665 : lock->owner = self;
666 : }
667 : if (++(lock->depth) == 0) /* wraparound? */
668 : {
669 : lock->depth--;
670 : return EAGAIN;
671 : }
672 : return 0;
673 : }
674 :
675 : int
676 : glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
677 : {
678 : if (lock->owner != pthread_self ())
679 : return EPERM;
680 : if (lock->depth == 0)
681 : return EINVAL;
682 : if (--(lock->depth) == 0)
683 : {
684 : lock->owner = (pthread_t) 0;
685 : return pthread_mutex_unlock (&lock->mutex);
686 : }
687 : else
688 : return 0;
689 : }
690 :
691 : int
692 : glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
693 : {
694 : if (lock->owner != (pthread_t) 0)
695 : return EBUSY;
696 : return pthread_mutex_destroy (&lock->mutex);
697 : }
698 :
699 : # endif
700 :
701 : /* -------------------------- gl_once_t datatype -------------------------- */
702 :
703 : static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
704 :
705 : int
706 1 : glthread_once_singlethreaded (pthread_once_t *once_control)
707 : {
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 1 : char *firstbyte = (char *)once_control;
711 1 : if (*firstbyte == *(const char *)&fresh_once)
712 : {
713 : /* First time use of once_control. Invert the first byte. */
714 1 : *firstbyte = ~ *(const char *)&fresh_once;
715 1 : return 1;
716 : }
717 : else
718 0 : return 0;
719 : }
720 :
721 : # if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
722 :
723 : int
724 : glthread_once_multithreaded (pthread_once_t *once_control,
725 : void (*init_function) (void))
726 : {
727 : int err = pthread_once (once_control, init_function);
728 : if (err == ENOSYS)
729 : {
730 : /* This happens on FreeBSD 11: The pthread_once function in libc returns
731 : ENOSYS. */
732 : if (glthread_once_singlethreaded (once_control))
733 : init_function ();
734 : return 0;
735 : }
736 : return err;
737 : }
738 :
739 : # endif
740 :
741 : #endif
742 :
743 : /* ========================================================================= */
744 :
745 : #if USE_WINDOWS_THREADS
746 :
747 : #endif
748 :
749 : /* ========================================================================= */
|