summaryrefslogtreecommitdiffstats
path: root/gl/glthread
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2023-02-11 07:20:24 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2023-02-11 07:20:24 +0100
commitf867d7b44080fa9716deeff4476275f9a489879f (patch)
treebc964662fc3300dc626fb6d833d8ed5c8f46eca7 /gl/glthread
parent9734c439cba0a02b087e50789e94ec9b07754608 (diff)
parentc07206f2ccc2356aa74bc6813a94c2190017d44e (diff)
downloadmonitoring-plugins-f867d7b.tar.gz
Merge branch 'master' into curlfixes
Diffstat (limited to 'gl/glthread')
-rw-r--r--gl/glthread/lock.c866
-rw-r--r--gl/glthread/lock.h544
-rw-r--r--gl/glthread/threadlib.c55
3 files changed, 528 insertions, 937 deletions
diff --git a/gl/glthread/lock.c b/gl/glthread/lock.c
index f62aa301..82fb7553 100644
--- a/gl/glthread/lock.c
+++ b/gl/glthread/lock.c
@@ -1,22 +1,21 @@
1/* Locking in multithreaded situations. 1/* Locking in multithreaded situations.
2 Copyright (C) 2005-2013 Free Software Foundation, Inc. 2 Copyright (C) 2005-2023 Free Software Foundation, Inc.
3 3
4 This program is free software; you can redistribute it and/or modify 4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by 5 it under the terms of the GNU Lesser General Public License as
6 the Free Software Foundation; either version 3, or (at your option) 6 published by the Free Software Foundation; either version 2.1 of the
7 any later version. 7 License, or (at your option) any later version.
8 8
9 This program is distributed in the hope that it will be useful, 9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details. 12 GNU Lesser General Public License for more details.
13 13
14 You should have received a copy of the GNU General Public License 14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>. */ 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16 16
17/* Written by Bruno Haible <bruno@clisp.org>, 2005. 17/* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, 18 Based on GCC's gthr-posix.h, gthr-posix95.h. */
19 gthr-win32.h. */
20 19
21#include <config.h> 20#include <config.h>
22 21
@@ -24,15 +23,267 @@
24 23
25/* ========================================================================= */ 24/* ========================================================================= */
26 25
26#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
27
28/* -------------------------- gl_lock_t datatype -------------------------- */
29
30int
31glthread_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
39int
40glthread_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
49int
50glthread_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
59int
60glthread_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
70int
71glthread_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
83int
84glthread_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
110int
111glthread_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
137int
138glthread_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
192int
193glthread_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
205int
206glthread_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
214int
215glthread_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
224int
225glthread_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
234int
235glthread_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
27#if USE_POSIX_THREADS 249#if USE_POSIX_THREADS
28 250
29/* -------------------------- gl_lock_t datatype -------------------------- */ 251/* -------------------------- gl_lock_t datatype -------------------------- */
30 252
31/* ------------------------- gl_rwlock_t datatype ------------------------- */ 253/* ------------------------- gl_rwlock_t datatype ------------------------- */
32 254
33# if HAVE_PTHREAD_RWLOCK 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
262int
263glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
264{
265 pthread_rwlockattr_t attributes;
266 int err;
34 267
35# if !defined PTHREAD_RWLOCK_INITIALIZER 268 err = pthread_rwlockattr_init (&attributes);
269 if (err != 0)
270 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 err = pthread_rwlockattr_setkind_np (&attributes,
276 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
277 if (err == 0)
278 err = pthread_rwlock_init(lock, &attributes);
279 /* pthread_rwlockattr_destroy always returns 0. It cannot influence the
280 return value. */
281 pthread_rwlockattr_destroy (&attributes);
282 return err;
283}
284
285# endif
286# else
36 287
37int 288int
38glthread_rwlock_init_multithreaded (gl_rwlock_t *lock) 289glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
@@ -152,11 +403,9 @@ glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
152 if (err != 0) 403 if (err != 0)
153 return err; 404 return err;
154 /* Test whether only readers are currently running, and whether the runcount 405 /* Test whether only readers are currently running, and whether the runcount
155 field will not overflow. */ 406 field will not overflow, and whether no writer is waiting. The latter
156 /* POSIX says: "It is implementation-defined whether the calling thread 407 condition is because POSIX recommends that "write locks shall take
157 acquires the lock when a writer does not hold the lock and there are 408 precedence over read locks", to avoid "writer starvation". */
158 writers blocked on the lock." Let's say, no: give the writers a higher
159 priority. */
160 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0)) 409 while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
161 { 410 {
162 /* This thread has to wait for a while. Enqueue it among the 411 /* This thread has to wait for a while. Enqueue it among the
@@ -469,161 +718,25 @@ glthread_once_singlethreaded (pthread_once_t *once_control)
469 return 0; 718 return 0;
470} 719}
471 720
472#endif 721# if !(PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK)
473
474/* ========================================================================= */
475
476#if USE_PTH_THREADS
477
478/* Use the GNU Pth threads library. */
479
480/* -------------------------- gl_lock_t datatype -------------------------- */
481
482/* ------------------------- gl_rwlock_t datatype ------------------------- */
483
484/* --------------------- gl_recursive_lock_t datatype --------------------- */
485
486/* -------------------------- gl_once_t datatype -------------------------- */
487
488static void
489glthread_once_call (void *arg)
490{
491 void (**gl_once_temp_addr) (void) = (void (**) (void)) arg;
492 void (*initfunction) (void) = *gl_once_temp_addr;
493 initfunction ();
494}
495
496int
497glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void))
498{
499 void (*temp) (void) = initfunction;
500 return (!pth_once (once_control, glthread_once_call, &temp) ? errno : 0);
501}
502
503int
504glthread_once_singlethreaded (pth_once_t *once_control)
505{
506 /* We know that pth_once_t is an integer type. */
507 if (*once_control == PTH_ONCE_INIT)
508 {
509 /* First time use of once_control. Invert the marker. */
510 *once_control = ~ PTH_ONCE_INIT;
511 return 1;
512 }
513 else
514 return 0;
515}
516
517#endif
518
519/* ========================================================================= */
520
521#if USE_SOLARIS_THREADS
522
523/* Use the old Solaris threads library. */
524
525/* -------------------------- gl_lock_t datatype -------------------------- */
526
527/* ------------------------- gl_rwlock_t datatype ------------------------- */
528
529/* --------------------- gl_recursive_lock_t datatype --------------------- */
530 722
531int 723int
532glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock) 724glthread_once_multithreaded (pthread_once_t *once_control,
725 void (*init_function) (void))
533{ 726{
534 int err; 727 int err = pthread_once (once_control, init_function);
535 728 if (err == ENOSYS)
536 err = mutex_init (&lock->mutex, USYNC_THREAD, NULL);
537 if (err != 0)
538 return err;
539 lock->owner = (thread_t) 0;
540 lock->depth = 0;
541 return 0;
542}
543
544int
545glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
546{
547 thread_t self = thr_self ();
548 if (lock->owner != self)
549 { 729 {
550 int err; 730 /* This happens on FreeBSD 11: The pthread_once function in libc returns
551 731 ENOSYS. */
552 err = mutex_lock (&lock->mutex); 732 if (glthread_once_singlethreaded (once_control))
553 if (err != 0) 733 init_function ();
554 return err; 734 return 0;
555 lock->owner = self;
556 } 735 }
557 if (++(lock->depth) == 0) /* wraparound? */ 736 return err;
558 {
559 lock->depth--;
560 return EAGAIN;
561 }
562 return 0;
563} 737}
564 738
565int 739# endif
566glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
567{
568 if (lock->owner != thr_self ())
569 return EPERM;
570 if (lock->depth == 0)
571 return EINVAL;
572 if (--(lock->depth) == 0)
573 {
574 lock->owner = (thread_t) 0;
575 return mutex_unlock (&lock->mutex);
576 }
577 else
578 return 0;
579}
580
581int
582glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
583{
584 if (lock->owner != (thread_t) 0)
585 return EBUSY;
586 return mutex_destroy (&lock->mutex);
587}
588
589/* -------------------------- gl_once_t datatype -------------------------- */
590
591int
592glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void))
593{
594 if (!once_control->inited)
595 {
596 int err;
597
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);
601 if (err != 0)
602 return err;
603 if (!once_control->inited)
604 {
605 once_control->inited = 1;
606 initfunction ();
607 }
608 return mutex_unlock (&once_control->mutex);
609 }
610 else
611 return 0;
612}
613
614int
615glthread_once_singlethreaded (gl_once_t *once_control)
616{
617 /* We know that gl_once_t contains an integer type. */
618 if (!once_control->inited)
619 {
620 /* First time use of once_control. Invert the marker. */
621 once_control->inited = ~ 0;
622 return 1;
623 }
624 else
625 return 0;
626}
627 740
628#endif 741#endif
629 742
@@ -631,427 +744,6 @@ glthread_once_singlethreaded (gl_once_t *once_control)
631 744
632#if USE_WINDOWS_THREADS 745#if USE_WINDOWS_THREADS
633 746
634/* -------------------------- gl_lock_t datatype -------------------------- */
635
636void
637glthread_lock_init_func (gl_lock_t *lock)
638{
639 InitializeCriticalSection (&lock->lock);
640 lock->guard.done = 1;
641}
642
643int
644glthread_lock_lock_func (gl_lock_t *lock)
645{
646 if (!lock->guard.done)
647 {
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);
651 else
652 /* Yield the CPU while waiting for another thread to finish
653 initializing this lock. */
654 while (!lock->guard.done)
655 Sleep (0);
656 }
657 EnterCriticalSection (&lock->lock);
658 return 0;
659}
660
661int
662glthread_lock_unlock_func (gl_lock_t *lock)
663{
664 if (!lock->guard.done)
665 return EINVAL;
666 LeaveCriticalSection (&lock->lock);
667 return 0;
668}
669
670int
671glthread_lock_destroy_func (gl_lock_t *lock)
672{
673 if (!lock->guard.done)
674 return EINVAL;
675 DeleteCriticalSection (&lock->lock);
676 lock->guard.done = 0;
677 return 0;
678}
679
680/* ------------------------- gl_rwlock_t datatype ------------------------- */
681
682/* In this file, the waitqueues are implemented as circular arrays. */
683#define gl_waitqueue_t gl_carray_waitqueue_t
684
685static void
686gl_waitqueue_init (gl_waitqueue_t *wq)
687{
688 wq->array = NULL;
689 wq->count = 0;
690 wq->alloc = 0;
691 wq->offset = 0;
692}
693
694/* Enqueues the current thread, represented by an event, in a wait queue.
695 Returns INVALID_HANDLE_VALUE if an allocation failure occurs. */
696static HANDLE
697gl_waitqueue_add (gl_waitqueue_t *wq)
698{
699 HANDLE event;
700 unsigned int index;
701
702 if (wq->count == wq->alloc)
703 {
704 unsigned int new_alloc = 2 * wq->alloc + 1;
705 HANDLE *new_array =
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. */
712 if (wq->offset > 0)
713 {
714 unsigned int old_count = wq->count;
715 unsigned int old_alloc = wq->alloc;
716 unsigned int old_offset = wq->offset;
717 unsigned int i;
718 if (old_offset + old_count > old_alloc)
719 {
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];
723 }
724 for (i = 0; i < old_count; i++)
725 new_array[i] = new_array[old_offset + i];
726 wq->offset = 0;
727 }
728 wq->array = new_array;
729 wq->alloc = new_alloc;
730 }
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)
739 index -= wq->alloc;
740 wq->array[index] = event;
741 wq->count++;
742 return event;
743}
744
745/* Notifies the first thread from a wait queue and dequeues it. */
746static void
747gl_waitqueue_notify_first (gl_waitqueue_t *wq)
748{
749 SetEvent (wq->array[wq->offset + 0]);
750 wq->offset++;
751 wq->count--;
752 if (wq->count == 0 || wq->offset == wq->alloc)
753 wq->offset = 0;
754}
755
756/* Notifies all threads from a wait queue and dequeues them all. */
757static void
758gl_waitqueue_notify_all (gl_waitqueue_t *wq)
759{
760 unsigned int i;
761
762 for (i = 0; i < wq->count; i++)
763 {
764 unsigned int index = wq->offset + i;
765 if (index >= wq->alloc)
766 index -= wq->alloc;
767 SetEvent (wq->array[index]);
768 }
769 wq->count = 0;
770 wq->offset = 0;
771}
772
773void
774glthread_rwlock_init_func (gl_rwlock_t *lock)
775{
776 InitializeCriticalSection (&lock->lock);
777 gl_waitqueue_init (&lock->waiting_readers);
778 gl_waitqueue_init (&lock->waiting_writers);
779 lock->runcount = 0;
780 lock->guard.done = 1;
781}
782
783int
784glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
785{
786 if (!lock->guard.done)
787 {
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);
791 else
792 /* Yield the CPU while waiting for another thread to finish
793 initializing this lock. */
794 while (!lock->guard.done)
795 Sleep (0);
796 }
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))
801 {
802 /* This thread has to wait for a while. Enqueue it among the
803 waiting_readers. */
804 HANDLE event = gl_waitqueue_add (&lock->waiting_readers);
805 if (event != INVALID_HANDLE_VALUE)
806 {
807 DWORD result;
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)
812 abort ();
813 CloseHandle (event);
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))
817 abort ();
818 return 0;
819 }
820 else
821 {
822 /* Allocation failure. Weird. */
823 do
824 {
825 LeaveCriticalSection (&lock->lock);
826 Sleep (1);
827 EnterCriticalSection (&lock->lock);
828 }
829 while (!(lock->runcount + 1 > 0));
830 }
831 }
832 lock->runcount++;
833 LeaveCriticalSection (&lock->lock);
834 return 0;
835}
836
837int
838glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
839{
840 if (!lock->guard.done)
841 {
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);
845 else
846 /* Yield the CPU while waiting for another thread to finish
847 initializing this lock. */
848 while (!lock->guard.done)
849 Sleep (0);
850 }
851 EnterCriticalSection (&lock->lock);
852 /* Test whether no readers or writers are currently running. */
853 if (!(lock->runcount == 0))
854 {
855 /* This thread has to wait for a while. Enqueue it among the
856 waiting_writers. */
857 HANDLE event = gl_waitqueue_add (&lock->waiting_writers);
858 if (event != INVALID_HANDLE_VALUE)
859 {
860 DWORD result;
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)
865 abort ();
866 CloseHandle (event);
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))
870 abort ();
871 return 0;
872 }
873 else
874 {
875 /* Allocation failure. Weird. */
876 do
877 {
878 LeaveCriticalSection (&lock->lock);
879 Sleep (1);
880 EnterCriticalSection (&lock->lock);
881 }
882 while (!(lock->runcount == 0));
883 }
884 }
885 lock->runcount--; /* runcount becomes -1 */
886 LeaveCriticalSection (&lock->lock);
887 return 0;
888}
889
890int
891glthread_rwlock_unlock_func (gl_rwlock_t *lock)
892{
893 if (!lock->guard.done)
894 return EINVAL;
895 EnterCriticalSection (&lock->lock);
896 if (lock->runcount < 0)
897 {
898 /* Drop a writer lock. */
899 if (!(lock->runcount == -1))
900 abort ();
901 lock->runcount = 0;
902 }
903 else
904 {
905 /* Drop a reader lock. */
906 if (!(lock->runcount > 0))
907 {
908 LeaveCriticalSection (&lock->lock);
909 return EPERM;
910 }
911 lock->runcount--;
912 }
913 if (lock->runcount == 0)
914 {
915 /* POSIX recommends that "write locks shall take precedence over read
916 locks", to avoid "writer starvation". */
917 if (lock->waiting_writers.count > 0)
918 {
919 /* Wake up one of the waiting writers. */
920 lock->runcount--;
921 gl_waitqueue_notify_first (&lock->waiting_writers);
922 }
923 else
924 {
925 /* Wake up all waiting readers. */
926 lock->runcount += lock->waiting_readers.count;
927 gl_waitqueue_notify_all (&lock->waiting_readers);
928 }
929 }
930 LeaveCriticalSection (&lock->lock);
931 return 0;
932}
933
934int
935glthread_rwlock_destroy_func (gl_rwlock_t *lock)
936{
937 if (!lock->guard.done)
938 return EINVAL;
939 if (lock->runcount != 0)
940 return EBUSY;
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;
947 return 0;
948}
949
950/* --------------------- gl_recursive_lock_t datatype --------------------- */
951
952void
953glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
954{
955 lock->owner = 0;
956 lock->depth = 0;
957 InitializeCriticalSection (&lock->lock);
958 lock->guard.done = 1;
959}
960
961int
962glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
963{
964 if (!lock->guard.done)
965 {
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);
969 else
970 /* Yield the CPU while waiting for another thread to finish
971 initializing this lock. */
972 while (!lock->guard.done)
973 Sleep (0);
974 }
975 {
976 DWORD self = GetCurrentThreadId ();
977 if (lock->owner != self)
978 {
979 EnterCriticalSection (&lock->lock);
980 lock->owner = self;
981 }
982 if (++(lock->depth) == 0) /* wraparound? */
983 {
984 lock->depth--;
985 return EAGAIN;
986 }
987 }
988 return 0;
989}
990
991int
992glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
993{
994 if (lock->owner != GetCurrentThreadId ())
995 return EPERM;
996 if (lock->depth == 0)
997 return EINVAL;
998 if (--(lock->depth) == 0)
999 {
1000 lock->owner = 0;
1001 LeaveCriticalSection (&lock->lock);
1002 }
1003 return 0;
1004}
1005
1006int
1007glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
1008{
1009 if (lock->owner != 0)
1010 return EBUSY;
1011 DeleteCriticalSection (&lock->lock);
1012 lock->guard.done = 0;
1013 return 0;
1014}
1015
1016/* -------------------------- gl_once_t datatype -------------------------- */
1017
1018void
1019glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
1020{
1021 if (once_control->inited <= 0)
1022 {
1023 if (InterlockedIncrement (&once_control->started) == 0)
1024 {
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;
1029 initfunction ();
1030 once_control->inited = 1;
1031 LeaveCriticalSection (&once_control->lock);
1032 }
1033 else
1034 {
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)
1041 Sleep (0);
1042 if (once_control->inited <= 0)
1043 {
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))
1049 abort ();
1050 }
1051 }
1052 }
1053}
1054
1055#endif 747#endif
1056 748
1057/* ========================================================================= */ 749/* ========================================================================= */
diff --git a/gl/glthread/lock.h b/gl/glthread/lock.h
index d20bbdef..ae3ee2d6 100644
--- a/gl/glthread/lock.h
+++ b/gl/glthread/lock.h
@@ -1,22 +1,21 @@
1/* Locking in multithreaded situations. 1/* Locking in multithreaded situations.
2 Copyright (C) 2005-2013 Free Software Foundation, Inc. 2 Copyright (C) 2005-2023 Free Software Foundation, Inc.
3 3
4 This program is free software; you can redistribute it and/or modify 4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by 5 it under the terms of the GNU Lesser General Public License as
6 the Free Software Foundation; either version 3, or (at your option) 6 published by the Free Software Foundation; either version 2.1 of the
7 any later version. 7 License, or (at your option) any later version.
8 8
9 This program is distributed in the hope that it will be useful, 9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details. 12 GNU Lesser General Public License for more details.
13 13
14 You should have received a copy of the GNU General Public License 14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>. */ 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16 16
17/* Written by Bruno Haible <bruno@clisp.org>, 2005. 17/* Written by Bruno Haible <bruno@clisp.org>, 2005.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, 18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */
19 gthr-win32.h. */
20 19
21/* This file contains locking primitives for use with a given thread library. 20/* This file contains locking primitives for use with a given thread library.
22 It does not contain primitives for creating threads or for other 21 It does not contain primitives for creating threads or for other
@@ -81,6 +80,127 @@
81#include <errno.h> 80#include <errno.h>
82#include <stdlib.h> 81#include <stdlib.h>
83 82
83#if !defined c11_threads_in_use
84# if HAVE_THREADS_H && USE_POSIX_THREADS_FROM_LIBC
85# define c11_threads_in_use() 1
86# elif HAVE_THREADS_H && USE_POSIX_THREADS_WEAK
87# include <threads.h>
88# pragma weak thrd_exit
89# define c11_threads_in_use() (thrd_exit != NULL)
90# else
91# define c11_threads_in_use() 0
92# endif
93#endif
94
95/* ========================================================================= */
96
97#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
98
99/* Use the ISO C threads library. */
100
101# include <threads.h>
102
103# ifdef __cplusplus
104extern "C" {
105# endif
106
107/* -------------------------- gl_lock_t datatype -------------------------- */
108
109typedef struct
110 {
111 int volatile init_needed;
112 once_flag init_once;
113 void (*init_func) (void);
114 mtx_t mutex;
115 }
116 gl_lock_t;
117# define gl_lock_define(STORAGECLASS, NAME) \
118 STORAGECLASS gl_lock_t NAME;
119# define gl_lock_define_initialized(STORAGECLASS, NAME) \
120 static void _atomic_init_##NAME (void); \
121 STORAGECLASS gl_lock_t NAME = \
122 { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
123 static void _atomic_init_##NAME (void) \
124 { \
125 if (glthread_lock_init (&(NAME))) \
126 abort (); \
127 }
128extern int glthread_lock_init (gl_lock_t *lock);
129extern int glthread_lock_lock (gl_lock_t *lock);
130extern int glthread_lock_unlock (gl_lock_t *lock);
131extern int glthread_lock_destroy (gl_lock_t *lock);
132
133/* ------------------------- gl_rwlock_t datatype ------------------------- */
134
135typedef struct
136 {
137 int volatile init_needed;
138 once_flag init_once;
139 void (*init_func) (void);
140 mtx_t lock; /* protects the remaining fields */
141 cnd_t waiting_readers; /* waiting readers */
142 cnd_t waiting_writers; /* waiting writers */
143 unsigned int waiting_writers_count; /* number of waiting writers */
144 int runcount; /* number of readers running, or -1 when a writer runs */
145 }
146 gl_rwlock_t;
147# define gl_rwlock_define(STORAGECLASS, NAME) \
148 STORAGECLASS gl_rwlock_t NAME;
149# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
150 static void _atomic_init_##NAME (void); \
151 STORAGECLASS gl_rwlock_t NAME = \
152 { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
153 static void _atomic_init_##NAME (void) \
154 { \
155 if (glthread_rwlock_init (&(NAME))) \
156 abort (); \
157 }
158extern int glthread_rwlock_init (gl_rwlock_t *lock);
159extern int glthread_rwlock_rdlock (gl_rwlock_t *lock);
160extern int glthread_rwlock_wrlock (gl_rwlock_t *lock);
161extern int glthread_rwlock_unlock (gl_rwlock_t *lock);
162extern int glthread_rwlock_destroy (gl_rwlock_t *lock);
163
164/* --------------------- gl_recursive_lock_t datatype --------------------- */
165
166typedef struct
167 {
168 int volatile init_needed;
169 once_flag init_once;
170 void (*init_func) (void);
171 mtx_t mutex;
172 }
173 gl_recursive_lock_t;
174# define gl_recursive_lock_define(STORAGECLASS, NAME) \
175 STORAGECLASS gl_recursive_lock_t NAME;
176# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
177 static void _atomic_init_##NAME (void); \
178 STORAGECLASS gl_recursive_lock_t NAME = \
179 { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
180 static void _atomic_init_##NAME (void) \
181 { \
182 if (glthread_recursive_lock_init (&(NAME))) \
183 abort (); \
184 }
185extern int glthread_recursive_lock_init (gl_recursive_lock_t *lock);
186extern int glthread_recursive_lock_lock (gl_recursive_lock_t *lock);
187extern int glthread_recursive_lock_unlock (gl_recursive_lock_t *lock);
188extern int glthread_recursive_lock_destroy (gl_recursive_lock_t *lock);
189
190/* -------------------------- gl_once_t datatype -------------------------- */
191
192typedef once_flag gl_once_t;
193# define gl_once_define(STORAGECLASS, NAME) \
194 STORAGECLASS once_flag NAME = ONCE_FLAG_INIT;
195# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
196 (call_once (ONCE_CONTROL, INITFUNCTION), 0)
197
198# ifdef __cplusplus
199}
200# endif
201
202#endif
203
84/* ========================================================================= */ 204/* ========================================================================= */
85 205
86#if USE_POSIX_THREADS 206#if USE_POSIX_THREADS
@@ -139,13 +259,25 @@ extern int glthread_in_use (void);
139# pragma weak pthread_mutexattr_init 259# pragma weak pthread_mutexattr_init
140# pragma weak pthread_mutexattr_settype 260# pragma weak pthread_mutexattr_settype
141# pragma weak pthread_mutexattr_destroy 261# pragma weak pthread_mutexattr_destroy
262# pragma weak pthread_rwlockattr_init
263# if __GNU_LIBRARY__ > 1
264# pragma weak pthread_rwlockattr_setkind_np
265# endif
266# pragma weak pthread_rwlockattr_destroy
142# ifndef pthread_self 267# ifndef pthread_self
143# pragma weak pthread_self 268# pragma weak pthread_self
144# endif 269# endif
145 270
146# if !PTHREAD_IN_USE_DETECTION_HARD 271# if !PTHREAD_IN_USE_DETECTION_HARD
147# pragma weak pthread_cancel 272 /* Considering all platforms with USE_POSIX_THREADS_WEAK, only few symbols
148# define pthread_in_use() (pthread_cancel != NULL) 273 can be used to determine whether libpthread is in use. These are:
274 pthread_mutexattr_gettype
275 pthread_rwlockattr_destroy
276 pthread_rwlockattr_init
277 */
278# pragma weak pthread_mutexattr_gettype
279# define pthread_in_use() \
280 (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
149# endif 281# endif
150 282
151# else 283# else
@@ -176,19 +308,32 @@ typedef pthread_mutex_t gl_lock_t;
176 308
177/* ------------------------- gl_rwlock_t datatype ------------------------- */ 309/* ------------------------- gl_rwlock_t datatype ------------------------- */
178 310
179# if HAVE_PTHREAD_RWLOCK 311# if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
180 312
181# ifdef PTHREAD_RWLOCK_INITIALIZER 313# if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
182 314
183typedef pthread_rwlock_t gl_rwlock_t; 315typedef pthread_rwlock_t gl_rwlock_t;
184# define gl_rwlock_define(STORAGECLASS, NAME) \ 316# define gl_rwlock_define(STORAGECLASS, NAME) \
185 STORAGECLASS pthread_rwlock_t NAME; 317 STORAGECLASS pthread_rwlock_t NAME;
186# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ 318# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
187 STORAGECLASS pthread_rwlock_t NAME = gl_rwlock_initializer; 319 STORAGECLASS pthread_rwlock_t NAME = gl_rwlock_initializer;
188# define gl_rwlock_initializer \ 320# if HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
189 PTHREAD_RWLOCK_INITIALIZER 321# if defined PTHREAD_RWLOCK_INITIALIZER
190# define glthread_rwlock_init(LOCK) \ 322# define gl_rwlock_initializer \
191 (pthread_in_use () ? pthread_rwlock_init (LOCK, NULL) : 0) 323 PTHREAD_RWLOCK_INITIALIZER
324# else
325# define gl_rwlock_initializer \
326 PTHREAD_RWLOCK_INITIALIZER_NP
327# endif
328# define glthread_rwlock_init(LOCK) \
329 (pthread_in_use () ? pthread_rwlock_init (LOCK, NULL) : 0)
330# else /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
331# define gl_rwlock_initializer \
332 PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP
333# define glthread_rwlock_init(LOCK) \
334 (pthread_in_use () ? glthread_rwlock_init_for_glibc (LOCK) : 0)
335extern int glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock);
336# endif
192# define glthread_rwlock_rdlock(LOCK) \ 337# define glthread_rwlock_rdlock(LOCK) \
193 (pthread_in_use () ? pthread_rwlock_rdlock (LOCK) : 0) 338 (pthread_in_use () ? pthread_rwlock_rdlock (LOCK) : 0)
194# define glthread_rwlock_wrlock(LOCK) \ 339# define glthread_rwlock_wrlock(LOCK) \
@@ -362,248 +507,20 @@ extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *l
362typedef pthread_once_t gl_once_t; 507typedef pthread_once_t gl_once_t;
363# define gl_once_define(STORAGECLASS, NAME) \ 508# define gl_once_define(STORAGECLASS, NAME) \
364 STORAGECLASS pthread_once_t NAME = PTHREAD_ONCE_INIT; 509 STORAGECLASS pthread_once_t NAME = PTHREAD_ONCE_INIT;
365# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ 510# if PTHREAD_IN_USE_DETECTION_HARD || USE_POSIX_THREADS_WEAK
366 (pthread_in_use () \ 511# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
367 ? pthread_once (ONCE_CONTROL, INITFUNCTION) \ 512 (pthread_in_use () \
368 : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0)) 513 ? pthread_once (ONCE_CONTROL, INITFUNCTION) \
369extern int glthread_once_singlethreaded (pthread_once_t *once_control); 514 : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0))
370
371# ifdef __cplusplus
372}
373# endif
374
375#endif
376
377/* ========================================================================= */
378
379#if USE_PTH_THREADS
380
381/* Use the GNU Pth threads library. */
382
383# include <pth.h>
384
385# ifdef __cplusplus
386extern "C" {
387# endif
388
389# if USE_PTH_THREADS_WEAK
390
391/* Use weak references to the GNU Pth threads library. */
392
393# pragma weak pth_mutex_init
394# pragma weak pth_mutex_acquire
395# pragma weak pth_mutex_release
396# pragma weak pth_rwlock_init
397# pragma weak pth_rwlock_acquire
398# pragma weak pth_rwlock_release
399# pragma weak pth_once
400
401# pragma weak pth_cancel
402# define pth_in_use() (pth_cancel != NULL)
403
404# else 515# else
405 516# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
406# define pth_in_use() 1 517 (pthread_in_use () \
407 518 ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION) \
519 : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0))
520extern int glthread_once_multithreaded (pthread_once_t *once_control,
521 void (*init_function) (void));
408# endif 522# endif
409 523extern int glthread_once_singlethreaded (pthread_once_t *once_control);
410/* -------------------------- gl_lock_t datatype -------------------------- */
411
412typedef pth_mutex_t gl_lock_t;
413# define gl_lock_define(STORAGECLASS, NAME) \
414 STORAGECLASS pth_mutex_t NAME;
415# define gl_lock_define_initialized(STORAGECLASS, NAME) \
416 STORAGECLASS pth_mutex_t NAME = gl_lock_initializer;
417# define gl_lock_initializer \
418 PTH_MUTEX_INIT
419# define glthread_lock_init(LOCK) \
420 (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0)
421# define glthread_lock_lock(LOCK) \
422 (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0)
423# define glthread_lock_unlock(LOCK) \
424 (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0)
425# define glthread_lock_destroy(LOCK) \
426 ((void)(LOCK), 0)
427
428/* ------------------------- gl_rwlock_t datatype ------------------------- */
429
430typedef pth_rwlock_t gl_rwlock_t;
431# define gl_rwlock_define(STORAGECLASS, NAME) \
432 STORAGECLASS pth_rwlock_t NAME;
433# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
434 STORAGECLASS pth_rwlock_t NAME = gl_rwlock_initializer;
435# define gl_rwlock_initializer \
436 PTH_RWLOCK_INIT
437# define glthread_rwlock_init(LOCK) \
438 (pth_in_use () && !pth_rwlock_init (LOCK) ? errno : 0)
439# define glthread_rwlock_rdlock(LOCK) \
440 (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RD, 0, NULL) ? errno : 0)
441# define glthread_rwlock_wrlock(LOCK) \
442 (pth_in_use () && !pth_rwlock_acquire (LOCK, PTH_RWLOCK_RW, 0, NULL) ? errno : 0)
443# define glthread_rwlock_unlock(LOCK) \
444 (pth_in_use () && !pth_rwlock_release (LOCK) ? errno : 0)
445# define glthread_rwlock_destroy(LOCK) \
446 ((void)(LOCK), 0)
447
448/* --------------------- gl_recursive_lock_t datatype --------------------- */
449
450/* In Pth, mutexes are recursive by default. */
451typedef pth_mutex_t gl_recursive_lock_t;
452# define gl_recursive_lock_define(STORAGECLASS, NAME) \
453 STORAGECLASS pth_mutex_t NAME;
454# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
455 STORAGECLASS pth_mutex_t NAME = gl_recursive_lock_initializer;
456# define gl_recursive_lock_initializer \
457 PTH_MUTEX_INIT
458# define glthread_recursive_lock_init(LOCK) \
459 (pth_in_use () && !pth_mutex_init (LOCK) ? errno : 0)
460# define glthread_recursive_lock_lock(LOCK) \
461 (pth_in_use () && !pth_mutex_acquire (LOCK, 0, NULL) ? errno : 0)
462# define glthread_recursive_lock_unlock(LOCK) \
463 (pth_in_use () && !pth_mutex_release (LOCK) ? errno : 0)
464# define glthread_recursive_lock_destroy(LOCK) \
465 ((void)(LOCK), 0)
466
467/* -------------------------- gl_once_t datatype -------------------------- */
468
469typedef pth_once_t gl_once_t;
470# define gl_once_define(STORAGECLASS, NAME) \
471 STORAGECLASS pth_once_t NAME = PTH_ONCE_INIT;
472# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
473 (pth_in_use () \
474 ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION) \
475 : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0))
476extern int glthread_once_multithreaded (pth_once_t *once_control, void (*initfunction) (void));
477extern int glthread_once_singlethreaded (pth_once_t *once_control);
478
479# ifdef __cplusplus
480}
481# endif
482
483#endif
484
485/* ========================================================================= */
486
487#if USE_SOLARIS_THREADS
488
489/* Use the old Solaris threads library. */
490
491# include <thread.h>
492# include <synch.h>
493
494# ifdef __cplusplus
495extern "C" {
496# endif
497
498# if USE_SOLARIS_THREADS_WEAK
499
500/* Use weak references to the old Solaris threads library. */
501
502# pragma weak mutex_init
503# pragma weak mutex_lock
504# pragma weak mutex_unlock
505# pragma weak mutex_destroy
506# pragma weak rwlock_init
507# pragma weak rw_rdlock
508# pragma weak rw_wrlock
509# pragma weak rw_unlock
510# pragma weak rwlock_destroy
511# pragma weak thr_self
512
513# pragma weak thr_suspend
514# define thread_in_use() (thr_suspend != NULL)
515
516# else
517
518# define thread_in_use() 1
519
520# endif
521
522/* -------------------------- gl_lock_t datatype -------------------------- */
523
524typedef mutex_t gl_lock_t;
525# define gl_lock_define(STORAGECLASS, NAME) \
526 STORAGECLASS mutex_t NAME;
527# define gl_lock_define_initialized(STORAGECLASS, NAME) \
528 STORAGECLASS mutex_t NAME = gl_lock_initializer;
529# define gl_lock_initializer \
530 DEFAULTMUTEX
531# define glthread_lock_init(LOCK) \
532 (thread_in_use () ? mutex_init (LOCK, USYNC_THREAD, NULL) : 0)
533# define glthread_lock_lock(LOCK) \
534 (thread_in_use () ? mutex_lock (LOCK) : 0)
535# define glthread_lock_unlock(LOCK) \
536 (thread_in_use () ? mutex_unlock (LOCK) : 0)
537# define glthread_lock_destroy(LOCK) \
538 (thread_in_use () ? mutex_destroy (LOCK) : 0)
539
540/* ------------------------- gl_rwlock_t datatype ------------------------- */
541
542typedef rwlock_t gl_rwlock_t;
543# define gl_rwlock_define(STORAGECLASS, NAME) \
544 STORAGECLASS rwlock_t NAME;
545# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
546 STORAGECLASS rwlock_t NAME = gl_rwlock_initializer;
547# define gl_rwlock_initializer \
548 DEFAULTRWLOCK
549# define glthread_rwlock_init(LOCK) \
550 (thread_in_use () ? rwlock_init (LOCK, USYNC_THREAD, NULL) : 0)
551# define glthread_rwlock_rdlock(LOCK) \
552 (thread_in_use () ? rw_rdlock (LOCK) : 0)
553# define glthread_rwlock_wrlock(LOCK) \
554 (thread_in_use () ? rw_wrlock (LOCK) : 0)
555# define glthread_rwlock_unlock(LOCK) \
556 (thread_in_use () ? rw_unlock (LOCK) : 0)
557# define glthread_rwlock_destroy(LOCK) \
558 (thread_in_use () ? rwlock_destroy (LOCK) : 0)
559
560/* --------------------- gl_recursive_lock_t datatype --------------------- */
561
562/* Old Solaris threads did not have recursive locks.
563 We have to implement them ourselves. */
564
565typedef struct
566 {
567 mutex_t mutex;
568 thread_t owner;
569 unsigned long depth;
570 }
571 gl_recursive_lock_t;
572# define gl_recursive_lock_define(STORAGECLASS, NAME) \
573 STORAGECLASS gl_recursive_lock_t NAME;
574# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
575 STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer;
576# define gl_recursive_lock_initializer \
577 { DEFAULTMUTEX, (thread_t) 0, 0 }
578# define glthread_recursive_lock_init(LOCK) \
579 (thread_in_use () ? glthread_recursive_lock_init_multithreaded (LOCK) : 0)
580# define glthread_recursive_lock_lock(LOCK) \
581 (thread_in_use () ? glthread_recursive_lock_lock_multithreaded (LOCK) : 0)
582# define glthread_recursive_lock_unlock(LOCK) \
583 (thread_in_use () ? glthread_recursive_lock_unlock_multithreaded (LOCK) : 0)
584# define glthread_recursive_lock_destroy(LOCK) \
585 (thread_in_use () ? glthread_recursive_lock_destroy_multithreaded (LOCK) : 0)
586extern int glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock);
587extern int glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock);
588extern int glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock);
589extern int glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock);
590
591/* -------------------------- gl_once_t datatype -------------------------- */
592
593typedef struct
594 {
595 volatile int inited;
596 mutex_t mutex;
597 }
598 gl_once_t;
599# define gl_once_define(STORAGECLASS, NAME) \
600 STORAGECLASS gl_once_t NAME = { 0, DEFAULTMUTEX };
601# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
602 (thread_in_use () \
603 ? glthread_once_multithreaded (ONCE_CONTROL, INITFUNCTION) \
604 : (glthread_once_singlethreaded (ONCE_CONTROL) ? (INITFUNCTION (), 0) : 0))
605extern int glthread_once_multithreaded (gl_once_t *once_control, void (*initfunction) (void));
606extern int glthread_once_singlethreaded (gl_once_t *once_control);
607 524
608# ifdef __cplusplus 525# ifdef __cplusplus
609} 526}
@@ -618,6 +535,11 @@ extern int glthread_once_singlethreaded (gl_once_t *once_control);
618# define WIN32_LEAN_AND_MEAN /* avoid including junk */ 535# define WIN32_LEAN_AND_MEAN /* avoid including junk */
619# include <windows.h> 536# include <windows.h>
620 537
538# include "windows-mutex.h"
539# include "windows-rwlock.h"
540# include "windows-recmutex.h"
541# include "windows-once.h"
542
621# ifdef __cplusplus 543# ifdef __cplusplus
622extern "C" { 544extern "C" {
623# endif 545# endif
@@ -633,127 +555,69 @@ extern "C" {
633/* There is no way to statically initialize a CRITICAL_SECTION. It needs 555/* There is no way to statically initialize a CRITICAL_SECTION. It needs
634 to be done lazily, once only. For this we need spinlocks. */ 556 to be done lazily, once only. For this we need spinlocks. */
635 557
636typedef struct { volatile int done; volatile long started; } gl_spinlock_t;
637
638/* -------------------------- gl_lock_t datatype -------------------------- */ 558/* -------------------------- gl_lock_t datatype -------------------------- */
639 559
640typedef struct 560typedef glwthread_mutex_t gl_lock_t;
641 {
642 gl_spinlock_t guard; /* protects the initialization */
643 CRITICAL_SECTION lock;
644 }
645 gl_lock_t;
646# define gl_lock_define(STORAGECLASS, NAME) \ 561# define gl_lock_define(STORAGECLASS, NAME) \
647 STORAGECLASS gl_lock_t NAME; 562 STORAGECLASS gl_lock_t NAME;
648# define gl_lock_define_initialized(STORAGECLASS, NAME) \ 563# define gl_lock_define_initialized(STORAGECLASS, NAME) \
649 STORAGECLASS gl_lock_t NAME = gl_lock_initializer; 564 STORAGECLASS gl_lock_t NAME = gl_lock_initializer;
650# define gl_lock_initializer \ 565# define gl_lock_initializer \
651 { { 0, -1 } } 566 GLWTHREAD_MUTEX_INIT
652# define glthread_lock_init(LOCK) \ 567# define glthread_lock_init(LOCK) \
653 (glthread_lock_init_func (LOCK), 0) 568 (glwthread_mutex_init (LOCK), 0)
654# define glthread_lock_lock(LOCK) \ 569# define glthread_lock_lock(LOCK) \
655 glthread_lock_lock_func (LOCK) 570 glwthread_mutex_lock (LOCK)
656# define glthread_lock_unlock(LOCK) \ 571# define glthread_lock_unlock(LOCK) \
657 glthread_lock_unlock_func (LOCK) 572 glwthread_mutex_unlock (LOCK)
658# define glthread_lock_destroy(LOCK) \ 573# define glthread_lock_destroy(LOCK) \
659 glthread_lock_destroy_func (LOCK) 574 glwthread_mutex_destroy (LOCK)
660extern void glthread_lock_init_func (gl_lock_t *lock);
661extern int glthread_lock_lock_func (gl_lock_t *lock);
662extern int glthread_lock_unlock_func (gl_lock_t *lock);
663extern int glthread_lock_destroy_func (gl_lock_t *lock);
664 575
665/* ------------------------- gl_rwlock_t datatype ------------------------- */ 576/* ------------------------- gl_rwlock_t datatype ------------------------- */
666 577
667/* It is impossible to implement read-write locks using plain locks, without 578typedef glwthread_rwlock_t gl_rwlock_t;
668 introducing an extra thread dedicated to managing read-write locks.
669 Therefore here we need to use the low-level Event type. */
670
671typedef struct
672 {
673 HANDLE *array; /* array of waiting threads, each represented by an event */
674 unsigned int count; /* number of waiting threads */
675 unsigned int alloc; /* length of allocated array */
676 unsigned int offset; /* index of first waiting thread in array */
677 }
678 gl_carray_waitqueue_t;
679typedef struct
680 {
681 gl_spinlock_t guard; /* protects the initialization */
682 CRITICAL_SECTION lock; /* protects the remaining fields */
683 gl_carray_waitqueue_t waiting_readers; /* waiting readers */
684 gl_carray_waitqueue_t waiting_writers; /* waiting writers */
685 int runcount; /* number of readers running, or -1 when a writer runs */
686 }
687 gl_rwlock_t;
688# define gl_rwlock_define(STORAGECLASS, NAME) \ 579# define gl_rwlock_define(STORAGECLASS, NAME) \
689 STORAGECLASS gl_rwlock_t NAME; 580 STORAGECLASS gl_rwlock_t NAME;
690# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \ 581# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
691 STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer; 582 STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer;
692# define gl_rwlock_initializer \ 583# define gl_rwlock_initializer \
693 { { 0, -1 } } 584 GLWTHREAD_RWLOCK_INIT
694# define glthread_rwlock_init(LOCK) \ 585# define glthread_rwlock_init(LOCK) \
695 (glthread_rwlock_init_func (LOCK), 0) 586 (glwthread_rwlock_init (LOCK), 0)
696# define glthread_rwlock_rdlock(LOCK) \ 587# define glthread_rwlock_rdlock(LOCK) \
697 glthread_rwlock_rdlock_func (LOCK) 588 glwthread_rwlock_rdlock (LOCK)
698# define glthread_rwlock_wrlock(LOCK) \ 589# define glthread_rwlock_wrlock(LOCK) \
699 glthread_rwlock_wrlock_func (LOCK) 590 glwthread_rwlock_wrlock (LOCK)
700# define glthread_rwlock_unlock(LOCK) \ 591# define glthread_rwlock_unlock(LOCK) \
701 glthread_rwlock_unlock_func (LOCK) 592 glwthread_rwlock_unlock (LOCK)
702# define glthread_rwlock_destroy(LOCK) \ 593# define glthread_rwlock_destroy(LOCK) \
703 glthread_rwlock_destroy_func (LOCK) 594 glwthread_rwlock_destroy (LOCK)
704extern void glthread_rwlock_init_func (gl_rwlock_t *lock);
705extern int glthread_rwlock_rdlock_func (gl_rwlock_t *lock);
706extern int glthread_rwlock_wrlock_func (gl_rwlock_t *lock);
707extern int glthread_rwlock_unlock_func (gl_rwlock_t *lock);
708extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock);
709 595
710/* --------------------- gl_recursive_lock_t datatype --------------------- */ 596/* --------------------- gl_recursive_lock_t datatype --------------------- */
711 597
712/* The native Windows documentation says that CRITICAL_SECTION already 598typedef glwthread_recmutex_t gl_recursive_lock_t;
713 implements a recursive lock. But we need not rely on it: It's easy to
714 implement a recursive lock without this assumption. */
715
716typedef struct
717 {
718 gl_spinlock_t guard; /* protects the initialization */
719 DWORD owner;
720 unsigned long depth;
721 CRITICAL_SECTION lock;
722 }
723 gl_recursive_lock_t;
724# define gl_recursive_lock_define(STORAGECLASS, NAME) \ 599# define gl_recursive_lock_define(STORAGECLASS, NAME) \
725 STORAGECLASS gl_recursive_lock_t NAME; 600 STORAGECLASS gl_recursive_lock_t NAME;
726# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \ 601# define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
727 STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer; 602 STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer;
728# define gl_recursive_lock_initializer \ 603# define gl_recursive_lock_initializer \
729 { { 0, -1 }, 0, 0 } 604 GLWTHREAD_RECMUTEX_INIT
730# define glthread_recursive_lock_init(LOCK) \ 605# define glthread_recursive_lock_init(LOCK) \
731 (glthread_recursive_lock_init_func (LOCK), 0) 606 (glwthread_recmutex_init (LOCK), 0)
732# define glthread_recursive_lock_lock(LOCK) \ 607# define glthread_recursive_lock_lock(LOCK) \
733 glthread_recursive_lock_lock_func (LOCK) 608 glwthread_recmutex_lock (LOCK)
734# define glthread_recursive_lock_unlock(LOCK) \ 609# define glthread_recursive_lock_unlock(LOCK) \
735 glthread_recursive_lock_unlock_func (LOCK) 610 glwthread_recmutex_unlock (LOCK)
736# define glthread_recursive_lock_destroy(LOCK) \ 611# define glthread_recursive_lock_destroy(LOCK) \
737 glthread_recursive_lock_destroy_func (LOCK) 612 glwthread_recmutex_destroy (LOCK)
738extern void glthread_recursive_lock_init_func (gl_recursive_lock_t *lock);
739extern int glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock);
740extern int glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock);
741extern int glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock);
742 613
743/* -------------------------- gl_once_t datatype -------------------------- */ 614/* -------------------------- gl_once_t datatype -------------------------- */
744 615
745typedef struct 616typedef glwthread_once_t gl_once_t;
746 {
747 volatile int inited;
748 volatile long started;
749 CRITICAL_SECTION lock;
750 }
751 gl_once_t;
752# define gl_once_define(STORAGECLASS, NAME) \ 617# define gl_once_define(STORAGECLASS, NAME) \
753 STORAGECLASS gl_once_t NAME = { -1, -1 }; 618 STORAGECLASS gl_once_t NAME = GLWTHREAD_ONCE_INIT;
754# define glthread_once(ONCE_CONTROL, INITFUNCTION) \ 619# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
755 (glthread_once_func (ONCE_CONTROL, INITFUNCTION), 0) 620 (glwthread_once (ONCE_CONTROL, INITFUNCTION), 0)
756extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (void));
757 621
758# ifdef __cplusplus 622# ifdef __cplusplus
759} 623}
@@ -763,7 +627,7 @@ extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (v
763 627
764/* ========================================================================= */ 628/* ========================================================================= */
765 629
766#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WINDOWS_THREADS) 630#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
767 631
768/* Provide dummy implementation if threads are not supported. */ 632/* Provide dummy implementation if threads are not supported. */
769 633
diff --git a/gl/glthread/threadlib.c b/gl/glthread/threadlib.c
index b4476573..5ecf827f 100644
--- a/gl/glthread/threadlib.c
+++ b/gl/glthread/threadlib.c
@@ -1,18 +1,18 @@
1/* Multithreading primitives. 1/* Multithreading primitives.
2 Copyright (C) 2005-2013 Free Software Foundation, Inc. 2 Copyright (C) 2005-2023 Free Software Foundation, Inc.
3 3
4 This program is free software; you can redistribute it and/or modify 4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by 5 it under the terms of the GNU Lesser General Public License as
6 the Free Software Foundation; either version 3, or (at your option) 6 published by the Free Software Foundation; either version 2.1 of the
7 any later version. 7 License, or (at your option) any later version.
8 8
9 This program is distributed in the hope that it will be useful, 9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details. 12 GNU Lesser General Public License for more details.
13 13
14 You should have received a copy of the GNU General Public License 14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>. */ 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16 16
17/* Written by Bruno Haible <bruno@clisp.org>, 2005. */ 17/* Written by Bruno Haible <bruno@clisp.org>, 2005. */
18 18
@@ -20,15 +20,48 @@
20 20
21/* ========================================================================= */ 21/* ========================================================================= */
22 22
23#if USE_POSIX_THREADS 23#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
24 24
25/* Use the POSIX threads library. */ 25/* Use the POSIX threads library. */
26 26
27# include <errno.h>
27# include <pthread.h> 28# include <pthread.h>
28# include <stdlib.h> 29# include <stdlib.h>
29 30
30# if PTHREAD_IN_USE_DETECTION_HARD 31# if PTHREAD_IN_USE_DETECTION_HARD
31 32
33# if defined __FreeBSD__ || defined __DragonFly__ /* FreeBSD */
34
35/* Test using pthread_key_create. */
36
37int
38glthread_in_use (void)
39{
40 static int tested;
41 static int result; /* 1: linked with -lpthread, 0: only with libc */
42
43 if (!tested)
44 {
45 pthread_key_t key;
46 int err = pthread_key_create (&key, NULL);
47
48 if (err == ENOSYS)
49 result = 0;
50 else
51 {
52 result = 1;
53 if (err == 0)
54 pthread_key_delete (key);
55 }
56 tested = 1;
57 }
58 return result;
59}
60
61# else /* Solaris, HP-UX */
62
63/* Test using pthread_create. */
64
32/* The function to be executed by a dummy thread. */ 65/* The function to be executed by a dummy thread. */
33static void * 66static void *
34dummy_thread_func (void *arg) 67dummy_thread_func (void *arg)
@@ -62,6 +95,8 @@ glthread_in_use (void)
62 return result; 95 return result;
63} 96}
64 97
98# endif
99
65# endif 100# endif
66 101
67#endif 102#endif