xref: /dragonfly/contrib/grep/lib/glthread/lock.c (revision 09d4459f)
1680a9cb8SJohn Marino /* Locking in multithreaded situations.
2*09d4459fSDaniel Fojt    Copyright (C) 2005-2020 Free Software Foundation, Inc.
3680a9cb8SJohn Marino 
4680a9cb8SJohn Marino    This program is free software; you can redistribute it and/or modify
5680a9cb8SJohn Marino    it under the terms of the GNU General Public License as published by
6680a9cb8SJohn Marino    the Free Software Foundation; either version 3, or (at your option)
7680a9cb8SJohn Marino    any later version.
8680a9cb8SJohn Marino 
9680a9cb8SJohn Marino    This program is distributed in the hope that it will be useful,
10680a9cb8SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
11680a9cb8SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12680a9cb8SJohn Marino    GNU General Public License for more details.
13680a9cb8SJohn Marino 
14680a9cb8SJohn Marino    You should have received a copy of the GNU General Public License
15*09d4459fSDaniel Fojt    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
16680a9cb8SJohn Marino 
17680a9cb8SJohn Marino /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18*09d4459fSDaniel Fojt    Based on GCC's gthr-posix.h, gthr-posix95.h.  */
19680a9cb8SJohn Marino 
20680a9cb8SJohn Marino #include <config.h>
21680a9cb8SJohn Marino 
22680a9cb8SJohn Marino #include "glthread/lock.h"
23680a9cb8SJohn Marino 
24680a9cb8SJohn Marino /* ========================================================================= */
25680a9cb8SJohn Marino 
26*09d4459fSDaniel Fojt #if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
27*09d4459fSDaniel Fojt 
28*09d4459fSDaniel Fojt /* -------------------------- gl_lock_t datatype -------------------------- */
29*09d4459fSDaniel Fojt 
30*09d4459fSDaniel Fojt int
glthread_lock_init(gl_lock_t * lock)31*09d4459fSDaniel Fojt glthread_lock_init (gl_lock_t *lock)
32*09d4459fSDaniel Fojt {
33*09d4459fSDaniel Fojt   if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
34*09d4459fSDaniel Fojt     return ENOMEM;
35*09d4459fSDaniel Fojt   lock->init_needed = 0;
36*09d4459fSDaniel Fojt   return 0;
37*09d4459fSDaniel Fojt }
38*09d4459fSDaniel Fojt 
39*09d4459fSDaniel Fojt int
glthread_lock_lock(gl_lock_t * lock)40*09d4459fSDaniel Fojt glthread_lock_lock (gl_lock_t *lock)
41*09d4459fSDaniel Fojt {
42*09d4459fSDaniel Fojt   if (lock->init_needed)
43*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
44*09d4459fSDaniel Fojt   if (mtx_lock (&lock->mutex) != thrd_success)
45*09d4459fSDaniel Fojt     return EAGAIN;
46*09d4459fSDaniel Fojt   return 0;
47*09d4459fSDaniel Fojt }
48*09d4459fSDaniel Fojt 
49*09d4459fSDaniel Fojt int
glthread_lock_unlock(gl_lock_t * lock)50*09d4459fSDaniel Fojt glthread_lock_unlock (gl_lock_t *lock)
51*09d4459fSDaniel Fojt {
52*09d4459fSDaniel Fojt   if (lock->init_needed)
53*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
54*09d4459fSDaniel Fojt   if (mtx_unlock (&lock->mutex) != thrd_success)
55*09d4459fSDaniel Fojt     return EINVAL;
56*09d4459fSDaniel Fojt   return 0;
57*09d4459fSDaniel Fojt }
58*09d4459fSDaniel Fojt 
59*09d4459fSDaniel Fojt int
glthread_lock_destroy(gl_lock_t * lock)60*09d4459fSDaniel Fojt glthread_lock_destroy (gl_lock_t *lock)
61*09d4459fSDaniel Fojt {
62*09d4459fSDaniel Fojt   if (lock->init_needed)
63*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
64*09d4459fSDaniel Fojt   mtx_destroy (&lock->mutex);
65*09d4459fSDaniel Fojt   return 0;
66*09d4459fSDaniel Fojt }
67*09d4459fSDaniel Fojt 
68*09d4459fSDaniel Fojt /* ------------------------- gl_rwlock_t datatype ------------------------- */
69*09d4459fSDaniel Fojt 
70*09d4459fSDaniel Fojt int
glthread_rwlock_init(gl_rwlock_t * lock)71*09d4459fSDaniel Fojt glthread_rwlock_init (gl_rwlock_t *lock)
72*09d4459fSDaniel Fojt {
73*09d4459fSDaniel Fojt   if (mtx_init (&lock->lock, mtx_plain) != thrd_success
74*09d4459fSDaniel Fojt       || cnd_init (&lock->waiting_readers) != thrd_success
75*09d4459fSDaniel Fojt       || cnd_init (&lock->waiting_writers) != thrd_success)
76*09d4459fSDaniel Fojt     return ENOMEM;
77*09d4459fSDaniel Fojt   lock->waiting_writers_count = 0;
78*09d4459fSDaniel Fojt   lock->runcount = 0;
79*09d4459fSDaniel Fojt   lock->init_needed = 0;
80*09d4459fSDaniel Fojt   return 0;
81*09d4459fSDaniel Fojt }
82*09d4459fSDaniel Fojt 
83*09d4459fSDaniel Fojt int
glthread_rwlock_rdlock(gl_rwlock_t * lock)84*09d4459fSDaniel Fojt glthread_rwlock_rdlock (gl_rwlock_t *lock)
85*09d4459fSDaniel Fojt {
86*09d4459fSDaniel Fojt   if (lock->init_needed)
87*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
88*09d4459fSDaniel Fojt   if (mtx_lock (&lock->lock) != thrd_success)
89*09d4459fSDaniel Fojt     return EAGAIN;
90*09d4459fSDaniel Fojt   /* Test whether only readers are currently running, and whether the runcount
91*09d4459fSDaniel Fojt      field will not overflow, and whether no writer is waiting.  The latter
92*09d4459fSDaniel Fojt      condition is because POSIX recommends that "write locks shall take
93*09d4459fSDaniel Fojt      precedence over read locks", to avoid "writer starvation".  */
94*09d4459fSDaniel Fojt   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
95*09d4459fSDaniel Fojt     {
96*09d4459fSDaniel Fojt       /* This thread has to wait for a while.  Enqueue it among the
97*09d4459fSDaniel Fojt          waiting_readers.  */
98*09d4459fSDaniel Fojt       if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
99*09d4459fSDaniel Fojt         {
100*09d4459fSDaniel Fojt           mtx_unlock (&lock->lock);
101*09d4459fSDaniel Fojt           return EINVAL;
102*09d4459fSDaniel Fojt         }
103*09d4459fSDaniel Fojt     }
104*09d4459fSDaniel Fojt   lock->runcount++;
105*09d4459fSDaniel Fojt   if (mtx_unlock (&lock->lock) != thrd_success)
106*09d4459fSDaniel Fojt     return EINVAL;
107*09d4459fSDaniel Fojt   return 0;
108*09d4459fSDaniel Fojt }
109*09d4459fSDaniel Fojt 
110*09d4459fSDaniel Fojt int
glthread_rwlock_wrlock(gl_rwlock_t * lock)111*09d4459fSDaniel Fojt glthread_rwlock_wrlock (gl_rwlock_t *lock)
112*09d4459fSDaniel Fojt {
113*09d4459fSDaniel Fojt   if (lock->init_needed)
114*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
115*09d4459fSDaniel Fojt   if (mtx_lock (&lock->lock) != thrd_success)
116*09d4459fSDaniel Fojt     return EAGAIN;
117*09d4459fSDaniel Fojt   /* Test whether no readers or writers are currently running.  */
118*09d4459fSDaniel Fojt   while (!(lock->runcount == 0))
119*09d4459fSDaniel Fojt     {
120*09d4459fSDaniel Fojt       /* This thread has to wait for a while.  Enqueue it among the
121*09d4459fSDaniel Fojt          waiting_writers.  */
122*09d4459fSDaniel Fojt       lock->waiting_writers_count++;
123*09d4459fSDaniel Fojt       if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
124*09d4459fSDaniel Fojt         {
125*09d4459fSDaniel Fojt           lock->waiting_writers_count--;
126*09d4459fSDaniel Fojt           mtx_unlock (&lock->lock);
127*09d4459fSDaniel Fojt           return EINVAL;
128*09d4459fSDaniel Fojt         }
129*09d4459fSDaniel Fojt       lock->waiting_writers_count--;
130*09d4459fSDaniel Fojt     }
131*09d4459fSDaniel Fojt   lock->runcount--; /* runcount becomes -1 */
132*09d4459fSDaniel Fojt   if (mtx_unlock (&lock->lock) != thrd_success)
133*09d4459fSDaniel Fojt     return EINVAL;
134*09d4459fSDaniel Fojt   return 0;
135*09d4459fSDaniel Fojt }
136*09d4459fSDaniel Fojt 
137*09d4459fSDaniel Fojt int
glthread_rwlock_unlock(gl_rwlock_t * lock)138*09d4459fSDaniel Fojt glthread_rwlock_unlock (gl_rwlock_t *lock)
139*09d4459fSDaniel Fojt {
140*09d4459fSDaniel Fojt   if (lock->init_needed)
141*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
142*09d4459fSDaniel Fojt   if (mtx_lock (&lock->lock) != thrd_success)
143*09d4459fSDaniel Fojt     return EAGAIN;
144*09d4459fSDaniel Fojt   if (lock->runcount < 0)
145*09d4459fSDaniel Fojt     {
146*09d4459fSDaniel Fojt       /* Drop a writer lock.  */
147*09d4459fSDaniel Fojt       if (!(lock->runcount == -1))
148*09d4459fSDaniel Fojt         {
149*09d4459fSDaniel Fojt           mtx_unlock (&lock->lock);
150*09d4459fSDaniel Fojt           return EINVAL;
151*09d4459fSDaniel Fojt         }
152*09d4459fSDaniel Fojt       lock->runcount = 0;
153*09d4459fSDaniel Fojt     }
154*09d4459fSDaniel Fojt   else
155*09d4459fSDaniel Fojt     {
156*09d4459fSDaniel Fojt       /* Drop a reader lock.  */
157*09d4459fSDaniel Fojt       if (!(lock->runcount > 0))
158*09d4459fSDaniel Fojt         {
159*09d4459fSDaniel Fojt           mtx_unlock (&lock->lock);
160*09d4459fSDaniel Fojt           return EINVAL;
161*09d4459fSDaniel Fojt         }
162*09d4459fSDaniel Fojt       lock->runcount--;
163*09d4459fSDaniel Fojt     }
164*09d4459fSDaniel Fojt   if (lock->runcount == 0)
165*09d4459fSDaniel Fojt     {
166*09d4459fSDaniel Fojt       /* POSIX recommends that "write locks shall take precedence over read
167*09d4459fSDaniel Fojt          locks", to avoid "writer starvation".  */
168*09d4459fSDaniel Fojt       if (lock->waiting_writers_count > 0)
169*09d4459fSDaniel Fojt         {
170*09d4459fSDaniel Fojt           /* Wake up one of the waiting writers.  */
171*09d4459fSDaniel Fojt           if (cnd_signal (&lock->waiting_writers) != thrd_success)
172*09d4459fSDaniel Fojt             {
173*09d4459fSDaniel Fojt               mtx_unlock (&lock->lock);
174*09d4459fSDaniel Fojt               return EINVAL;
175*09d4459fSDaniel Fojt             }
176*09d4459fSDaniel Fojt         }
177*09d4459fSDaniel Fojt       else
178*09d4459fSDaniel Fojt         {
179*09d4459fSDaniel Fojt           /* Wake up all waiting readers.  */
180*09d4459fSDaniel Fojt           if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
181*09d4459fSDaniel Fojt             {
182*09d4459fSDaniel Fojt               mtx_unlock (&lock->lock);
183*09d4459fSDaniel Fojt               return EINVAL;
184*09d4459fSDaniel Fojt             }
185*09d4459fSDaniel Fojt         }
186*09d4459fSDaniel Fojt     }
187*09d4459fSDaniel Fojt   if (mtx_unlock (&lock->lock) != thrd_success)
188*09d4459fSDaniel Fojt     return EINVAL;
189*09d4459fSDaniel Fojt   return 0;
190*09d4459fSDaniel Fojt }
191*09d4459fSDaniel Fojt 
192*09d4459fSDaniel Fojt int
glthread_rwlock_destroy(gl_rwlock_t * lock)193*09d4459fSDaniel Fojt glthread_rwlock_destroy (gl_rwlock_t *lock)
194*09d4459fSDaniel Fojt {
195*09d4459fSDaniel Fojt   if (lock->init_needed)
196*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
197*09d4459fSDaniel Fojt   mtx_destroy (&lock->lock);
198*09d4459fSDaniel Fojt   cnd_destroy (&lock->waiting_readers);
199*09d4459fSDaniel Fojt   cnd_destroy (&lock->waiting_writers);
200*09d4459fSDaniel Fojt   return 0;
201*09d4459fSDaniel Fojt }
202*09d4459fSDaniel Fojt 
203*09d4459fSDaniel Fojt /* --------------------- gl_recursive_lock_t datatype --------------------- */
204*09d4459fSDaniel Fojt 
205*09d4459fSDaniel Fojt int
glthread_recursive_lock_init(gl_recursive_lock_t * lock)206*09d4459fSDaniel Fojt glthread_recursive_lock_init (gl_recursive_lock_t *lock)
207*09d4459fSDaniel Fojt {
208*09d4459fSDaniel Fojt   if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
209*09d4459fSDaniel Fojt     return ENOMEM;
210*09d4459fSDaniel Fojt   lock->init_needed = 0;
211*09d4459fSDaniel Fojt   return 0;
212*09d4459fSDaniel Fojt }
213*09d4459fSDaniel Fojt 
214*09d4459fSDaniel Fojt int
glthread_recursive_lock_lock(gl_recursive_lock_t * lock)215*09d4459fSDaniel Fojt glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
216*09d4459fSDaniel Fojt {
217*09d4459fSDaniel Fojt   if (lock->init_needed)
218*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
219*09d4459fSDaniel Fojt   if (mtx_lock (&lock->mutex) != thrd_success)
220*09d4459fSDaniel Fojt     return EAGAIN;
221*09d4459fSDaniel Fojt   return 0;
222*09d4459fSDaniel Fojt }
223*09d4459fSDaniel Fojt 
224*09d4459fSDaniel Fojt int
glthread_recursive_lock_unlock(gl_recursive_lock_t * lock)225*09d4459fSDaniel Fojt glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
226*09d4459fSDaniel Fojt {
227*09d4459fSDaniel Fojt   if (lock->init_needed)
228*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
229*09d4459fSDaniel Fojt   if (mtx_unlock (&lock->mutex) != thrd_success)
230*09d4459fSDaniel Fojt     return EINVAL;
231*09d4459fSDaniel Fojt   return 0;
232*09d4459fSDaniel Fojt }
233*09d4459fSDaniel Fojt 
234*09d4459fSDaniel Fojt int
glthread_recursive_lock_destroy(gl_recursive_lock_t * lock)235*09d4459fSDaniel Fojt glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
236*09d4459fSDaniel Fojt {
237*09d4459fSDaniel Fojt   if (lock->init_needed)
238*09d4459fSDaniel Fojt     call_once (&lock->init_once, lock->init_func);
239*09d4459fSDaniel Fojt   mtx_destroy (&lock->mutex);
240*09d4459fSDaniel Fojt   return 0;
241*09d4459fSDaniel Fojt }
242*09d4459fSDaniel Fojt 
243*09d4459fSDaniel Fojt /* -------------------------- gl_once_t datatype -------------------------- */
244*09d4459fSDaniel Fojt 
245*09d4459fSDaniel Fojt #endif
246*09d4459fSDaniel Fojt 
247*09d4459fSDaniel Fojt /* ========================================================================= */
248*09d4459fSDaniel Fojt 
249680a9cb8SJohn Marino #if USE_POSIX_THREADS
250680a9cb8SJohn Marino 
251680a9cb8SJohn Marino /* -------------------------- gl_lock_t datatype -------------------------- */
252680a9cb8SJohn Marino 
253680a9cb8SJohn Marino /* ------------------------- gl_rwlock_t datatype ------------------------- */
254680a9cb8SJohn Marino 
255*09d4459fSDaniel Fojt # if HAVE_PTHREAD_RWLOCK && (HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER || (defined PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP && (__GNU_LIBRARY__ > 1)))
256680a9cb8SJohn Marino 
257*09d4459fSDaniel Fojt #  if defined PTHREAD_RWLOCK_INITIALIZER || defined PTHREAD_RWLOCK_INITIALIZER_NP
258*09d4459fSDaniel Fojt 
259*09d4459fSDaniel Fojt #   if !HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER
260*09d4459fSDaniel Fojt      /* glibc with bug https://sourceware.org/bugzilla/show_bug.cgi?id=13701 */
261*09d4459fSDaniel Fojt 
262*09d4459fSDaniel Fojt int
glthread_rwlock_init_for_glibc(pthread_rwlock_t * lock)263*09d4459fSDaniel Fojt glthread_rwlock_init_for_glibc (pthread_rwlock_t *lock)
264*09d4459fSDaniel Fojt {
265*09d4459fSDaniel Fojt   pthread_rwlockattr_t attributes;
266*09d4459fSDaniel Fojt   int err;
267*09d4459fSDaniel Fojt 
268*09d4459fSDaniel Fojt   err = pthread_rwlockattr_init (&attributes);
269*09d4459fSDaniel Fojt   if (err != 0)
270*09d4459fSDaniel Fojt     return err;
271*09d4459fSDaniel Fojt   /* Note: PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is the only value that
272*09d4459fSDaniel Fojt      causes the writer to be preferred. PTHREAD_RWLOCK_PREFER_WRITER_NP does not
273*09d4459fSDaniel Fojt      do this; see
274*09d4459fSDaniel Fojt      http://man7.org/linux/man-pages/man3/pthread_rwlockattr_setkind_np.3.html */
275*09d4459fSDaniel Fojt   err = pthread_rwlockattr_setkind_np (&attributes,
276*09d4459fSDaniel Fojt                                        PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
277*09d4459fSDaniel Fojt   if (err == 0)
278*09d4459fSDaniel Fojt     err = pthread_rwlock_init(lock, &attributes);
279*09d4459fSDaniel Fojt   /* pthread_rwlockattr_destroy always returns 0.  It cannot influence the
280*09d4459fSDaniel Fojt      return value.  */
281*09d4459fSDaniel Fojt   pthread_rwlockattr_destroy (&attributes);
282*09d4459fSDaniel Fojt   return err;
283*09d4459fSDaniel Fojt }
284*09d4459fSDaniel Fojt 
285*09d4459fSDaniel Fojt #   endif
286*09d4459fSDaniel Fojt #  else
287680a9cb8SJohn Marino 
288680a9cb8SJohn Marino int
glthread_rwlock_init_multithreaded(gl_rwlock_t * lock)289680a9cb8SJohn Marino glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
290680a9cb8SJohn Marino {
291680a9cb8SJohn Marino   int err;
292680a9cb8SJohn Marino 
293680a9cb8SJohn Marino   err = pthread_rwlock_init (&lock->rwlock, NULL);
294680a9cb8SJohn Marino   if (err != 0)
295680a9cb8SJohn Marino     return err;
296680a9cb8SJohn Marino   lock->initialized = 1;
297680a9cb8SJohn Marino   return 0;
298680a9cb8SJohn Marino }
299680a9cb8SJohn Marino 
300680a9cb8SJohn Marino int
glthread_rwlock_rdlock_multithreaded(gl_rwlock_t * lock)301680a9cb8SJohn Marino glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
302680a9cb8SJohn Marino {
303680a9cb8SJohn Marino   if (!lock->initialized)
304680a9cb8SJohn Marino     {
305680a9cb8SJohn Marino       int err;
306680a9cb8SJohn Marino 
307680a9cb8SJohn Marino       err = pthread_mutex_lock (&lock->guard);
308680a9cb8SJohn Marino       if (err != 0)
309680a9cb8SJohn Marino         return err;
310680a9cb8SJohn Marino       if (!lock->initialized)
311680a9cb8SJohn Marino         {
312680a9cb8SJohn Marino           err = glthread_rwlock_init_multithreaded (lock);
313680a9cb8SJohn Marino           if (err != 0)
314680a9cb8SJohn Marino             {
315680a9cb8SJohn Marino               pthread_mutex_unlock (&lock->guard);
316680a9cb8SJohn Marino               return err;
317680a9cb8SJohn Marino             }
318680a9cb8SJohn Marino         }
319680a9cb8SJohn Marino       err = pthread_mutex_unlock (&lock->guard);
320680a9cb8SJohn Marino       if (err != 0)
321680a9cb8SJohn Marino         return err;
322680a9cb8SJohn Marino     }
323680a9cb8SJohn Marino   return pthread_rwlock_rdlock (&lock->rwlock);
324680a9cb8SJohn Marino }
325680a9cb8SJohn Marino 
326680a9cb8SJohn Marino int
glthread_rwlock_wrlock_multithreaded(gl_rwlock_t * lock)327680a9cb8SJohn Marino glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
328680a9cb8SJohn Marino {
329680a9cb8SJohn Marino   if (!lock->initialized)
330680a9cb8SJohn Marino     {
331680a9cb8SJohn Marino       int err;
332680a9cb8SJohn Marino 
333680a9cb8SJohn Marino       err = pthread_mutex_lock (&lock->guard);
334680a9cb8SJohn Marino       if (err != 0)
335680a9cb8SJohn Marino         return err;
336680a9cb8SJohn Marino       if (!lock->initialized)
337680a9cb8SJohn Marino         {
338680a9cb8SJohn Marino           err = glthread_rwlock_init_multithreaded (lock);
339680a9cb8SJohn Marino           if (err != 0)
340680a9cb8SJohn Marino             {
341680a9cb8SJohn Marino               pthread_mutex_unlock (&lock->guard);
342680a9cb8SJohn Marino               return err;
343680a9cb8SJohn Marino             }
344680a9cb8SJohn Marino         }
345680a9cb8SJohn Marino       err = pthread_mutex_unlock (&lock->guard);
346680a9cb8SJohn Marino       if (err != 0)
347680a9cb8SJohn Marino         return err;
348680a9cb8SJohn Marino     }
349680a9cb8SJohn Marino   return pthread_rwlock_wrlock (&lock->rwlock);
350680a9cb8SJohn Marino }
351680a9cb8SJohn Marino 
352680a9cb8SJohn Marino int
glthread_rwlock_unlock_multithreaded(gl_rwlock_t * lock)353680a9cb8SJohn Marino glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
354680a9cb8SJohn Marino {
355680a9cb8SJohn Marino   if (!lock->initialized)
356680a9cb8SJohn Marino     return EINVAL;
357680a9cb8SJohn Marino   return pthread_rwlock_unlock (&lock->rwlock);
358680a9cb8SJohn Marino }
359680a9cb8SJohn Marino 
360680a9cb8SJohn Marino int
glthread_rwlock_destroy_multithreaded(gl_rwlock_t * lock)361680a9cb8SJohn Marino glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
362680a9cb8SJohn Marino {
363680a9cb8SJohn Marino   int err;
364680a9cb8SJohn Marino 
365680a9cb8SJohn Marino   if (!lock->initialized)
366680a9cb8SJohn Marino     return EINVAL;
367680a9cb8SJohn Marino   err = pthread_rwlock_destroy (&lock->rwlock);
368680a9cb8SJohn Marino   if (err != 0)
369680a9cb8SJohn Marino     return err;
370680a9cb8SJohn Marino   lock->initialized = 0;
371680a9cb8SJohn Marino   return 0;
372680a9cb8SJohn Marino }
373680a9cb8SJohn Marino 
374680a9cb8SJohn Marino #  endif
375680a9cb8SJohn Marino 
376680a9cb8SJohn Marino # else
377680a9cb8SJohn Marino 
378680a9cb8SJohn Marino int
glthread_rwlock_init_multithreaded(gl_rwlock_t * lock)379680a9cb8SJohn Marino glthread_rwlock_init_multithreaded (gl_rwlock_t *lock)
380680a9cb8SJohn Marino {
381680a9cb8SJohn Marino   int err;
382680a9cb8SJohn Marino 
383680a9cb8SJohn Marino   err = pthread_mutex_init (&lock->lock, NULL);
384680a9cb8SJohn Marino   if (err != 0)
385680a9cb8SJohn Marino     return err;
386680a9cb8SJohn Marino   err = pthread_cond_init (&lock->waiting_readers, NULL);
387680a9cb8SJohn Marino   if (err != 0)
388680a9cb8SJohn Marino     return err;
389680a9cb8SJohn Marino   err = pthread_cond_init (&lock->waiting_writers, NULL);
390680a9cb8SJohn Marino   if (err != 0)
391680a9cb8SJohn Marino     return err;
392680a9cb8SJohn Marino   lock->waiting_writers_count = 0;
393680a9cb8SJohn Marino   lock->runcount = 0;
394680a9cb8SJohn Marino   return 0;
395680a9cb8SJohn Marino }
396680a9cb8SJohn Marino 
397680a9cb8SJohn Marino int
glthread_rwlock_rdlock_multithreaded(gl_rwlock_t * lock)398680a9cb8SJohn Marino glthread_rwlock_rdlock_multithreaded (gl_rwlock_t *lock)
399680a9cb8SJohn Marino {
400680a9cb8SJohn Marino   int err;
401680a9cb8SJohn Marino 
402680a9cb8SJohn Marino   err = pthread_mutex_lock (&lock->lock);
403680a9cb8SJohn Marino   if (err != 0)
404680a9cb8SJohn Marino     return err;
405680a9cb8SJohn Marino   /* Test whether only readers are currently running, and whether the runcount
406*09d4459fSDaniel Fojt      field will not overflow, and whether no writer is waiting.  The latter
407*09d4459fSDaniel Fojt      condition is because POSIX recommends that "write locks shall take
408*09d4459fSDaniel Fojt      precedence over read locks", to avoid "writer starvation".  */
409680a9cb8SJohn Marino   while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
410680a9cb8SJohn Marino     {
411680a9cb8SJohn Marino       /* This thread has to wait for a while.  Enqueue it among the
412680a9cb8SJohn Marino          waiting_readers.  */
413680a9cb8SJohn Marino       err = pthread_cond_wait (&lock->waiting_readers, &lock->lock);
414680a9cb8SJohn Marino       if (err != 0)
415680a9cb8SJohn Marino         {
416680a9cb8SJohn Marino           pthread_mutex_unlock (&lock->lock);
417680a9cb8SJohn Marino           return err;
418680a9cb8SJohn Marino         }
419680a9cb8SJohn Marino     }
420680a9cb8SJohn Marino   lock->runcount++;
421680a9cb8SJohn Marino   return pthread_mutex_unlock (&lock->lock);
422680a9cb8SJohn Marino }
423680a9cb8SJohn Marino 
424680a9cb8SJohn Marino int
glthread_rwlock_wrlock_multithreaded(gl_rwlock_t * lock)425680a9cb8SJohn Marino glthread_rwlock_wrlock_multithreaded (gl_rwlock_t *lock)
426680a9cb8SJohn Marino {
427680a9cb8SJohn Marino   int err;
428680a9cb8SJohn Marino 
429680a9cb8SJohn Marino   err = pthread_mutex_lock (&lock->lock);
430680a9cb8SJohn Marino   if (err != 0)
431680a9cb8SJohn Marino     return err;
432680a9cb8SJohn Marino   /* Test whether no readers or writers are currently running.  */
433680a9cb8SJohn Marino   while (!(lock->runcount == 0))
434680a9cb8SJohn Marino     {
435680a9cb8SJohn Marino       /* This thread has to wait for a while.  Enqueue it among the
436680a9cb8SJohn Marino          waiting_writers.  */
437680a9cb8SJohn Marino       lock->waiting_writers_count++;
438680a9cb8SJohn Marino       err = pthread_cond_wait (&lock->waiting_writers, &lock->lock);
439680a9cb8SJohn Marino       if (err != 0)
440680a9cb8SJohn Marino         {
441680a9cb8SJohn Marino           lock->waiting_writers_count--;
442680a9cb8SJohn Marino           pthread_mutex_unlock (&lock->lock);
443680a9cb8SJohn Marino           return err;
444680a9cb8SJohn Marino         }
445680a9cb8SJohn Marino       lock->waiting_writers_count--;
446680a9cb8SJohn Marino     }
447680a9cb8SJohn Marino   lock->runcount--; /* runcount becomes -1 */
448680a9cb8SJohn Marino   return pthread_mutex_unlock (&lock->lock);
449680a9cb8SJohn Marino }
450680a9cb8SJohn Marino 
451680a9cb8SJohn Marino int
glthread_rwlock_unlock_multithreaded(gl_rwlock_t * lock)452680a9cb8SJohn Marino glthread_rwlock_unlock_multithreaded (gl_rwlock_t *lock)
453680a9cb8SJohn Marino {
454680a9cb8SJohn Marino   int err;
455680a9cb8SJohn Marino 
456680a9cb8SJohn Marino   err = pthread_mutex_lock (&lock->lock);
457680a9cb8SJohn Marino   if (err != 0)
458680a9cb8SJohn Marino     return err;
459680a9cb8SJohn Marino   if (lock->runcount < 0)
460680a9cb8SJohn Marino     {
461680a9cb8SJohn Marino       /* Drop a writer lock.  */
462680a9cb8SJohn Marino       if (!(lock->runcount == -1))
463680a9cb8SJohn Marino         {
464680a9cb8SJohn Marino           pthread_mutex_unlock (&lock->lock);
465680a9cb8SJohn Marino           return EINVAL;
466680a9cb8SJohn Marino         }
467680a9cb8SJohn Marino       lock->runcount = 0;
468680a9cb8SJohn Marino     }
469680a9cb8SJohn Marino   else
470680a9cb8SJohn Marino     {
471680a9cb8SJohn Marino       /* Drop a reader lock.  */
472680a9cb8SJohn Marino       if (!(lock->runcount > 0))
473680a9cb8SJohn Marino         {
474680a9cb8SJohn Marino           pthread_mutex_unlock (&lock->lock);
475680a9cb8SJohn Marino           return EINVAL;
476680a9cb8SJohn Marino         }
477680a9cb8SJohn Marino       lock->runcount--;
478680a9cb8SJohn Marino     }
479680a9cb8SJohn Marino   if (lock->runcount == 0)
480680a9cb8SJohn Marino     {
481680a9cb8SJohn Marino       /* POSIX recommends that "write locks shall take precedence over read
482680a9cb8SJohn Marino          locks", to avoid "writer starvation".  */
483680a9cb8SJohn Marino       if (lock->waiting_writers_count > 0)
484680a9cb8SJohn Marino         {
485680a9cb8SJohn Marino           /* Wake up one of the waiting writers.  */
486680a9cb8SJohn Marino           err = pthread_cond_signal (&lock->waiting_writers);
487680a9cb8SJohn Marino           if (err != 0)
488680a9cb8SJohn Marino             {
489680a9cb8SJohn Marino               pthread_mutex_unlock (&lock->lock);
490680a9cb8SJohn Marino               return err;
491680a9cb8SJohn Marino             }
492680a9cb8SJohn Marino         }
493680a9cb8SJohn Marino       else
494680a9cb8SJohn Marino         {
495680a9cb8SJohn Marino           /* Wake up all waiting readers.  */
496680a9cb8SJohn Marino           err = pthread_cond_broadcast (&lock->waiting_readers);
497680a9cb8SJohn Marino           if (err != 0)
498680a9cb8SJohn Marino             {
499680a9cb8SJohn Marino               pthread_mutex_unlock (&lock->lock);
500680a9cb8SJohn Marino               return err;
501680a9cb8SJohn Marino             }
502680a9cb8SJohn Marino         }
503680a9cb8SJohn Marino     }
504680a9cb8SJohn Marino   return pthread_mutex_unlock (&lock->lock);
505680a9cb8SJohn Marino }
506680a9cb8SJohn Marino 
507680a9cb8SJohn Marino int
glthread_rwlock_destroy_multithreaded(gl_rwlock_t * lock)508680a9cb8SJohn Marino glthread_rwlock_destroy_multithreaded (gl_rwlock_t *lock)
509680a9cb8SJohn Marino {
510680a9cb8SJohn Marino   int err;
511680a9cb8SJohn Marino 
512680a9cb8SJohn Marino   err = pthread_mutex_destroy (&lock->lock);
513680a9cb8SJohn Marino   if (err != 0)
514680a9cb8SJohn Marino     return err;
515680a9cb8SJohn Marino   err = pthread_cond_destroy (&lock->waiting_readers);
516680a9cb8SJohn Marino   if (err != 0)
517680a9cb8SJohn Marino     return err;
518680a9cb8SJohn Marino   err = pthread_cond_destroy (&lock->waiting_writers);
519680a9cb8SJohn Marino   if (err != 0)
520680a9cb8SJohn Marino     return err;
521680a9cb8SJohn Marino   return 0;
522680a9cb8SJohn Marino }
523680a9cb8SJohn Marino 
524680a9cb8SJohn Marino # endif
525680a9cb8SJohn Marino 
526680a9cb8SJohn Marino /* --------------------- gl_recursive_lock_t datatype --------------------- */
527680a9cb8SJohn Marino 
528680a9cb8SJohn Marino # if HAVE_PTHREAD_MUTEX_RECURSIVE
529680a9cb8SJohn Marino 
530680a9cb8SJohn Marino #  if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER || defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
531680a9cb8SJohn Marino 
532680a9cb8SJohn Marino int
glthread_recursive_lock_init_multithreaded(gl_recursive_lock_t * lock)533680a9cb8SJohn Marino glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
534680a9cb8SJohn Marino {
535680a9cb8SJohn Marino   pthread_mutexattr_t attributes;
536680a9cb8SJohn Marino   int err;
537680a9cb8SJohn Marino 
538680a9cb8SJohn Marino   err = pthread_mutexattr_init (&attributes);
539680a9cb8SJohn Marino   if (err != 0)
540680a9cb8SJohn Marino     return err;
541680a9cb8SJohn Marino   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
542680a9cb8SJohn Marino   if (err != 0)
543680a9cb8SJohn Marino     {
544680a9cb8SJohn Marino       pthread_mutexattr_destroy (&attributes);
545680a9cb8SJohn Marino       return err;
546680a9cb8SJohn Marino     }
547680a9cb8SJohn Marino   err = pthread_mutex_init (lock, &attributes);
548680a9cb8SJohn Marino   if (err != 0)
549680a9cb8SJohn Marino     {
550680a9cb8SJohn Marino       pthread_mutexattr_destroy (&attributes);
551680a9cb8SJohn Marino       return err;
552680a9cb8SJohn Marino     }
553680a9cb8SJohn Marino   err = pthread_mutexattr_destroy (&attributes);
554680a9cb8SJohn Marino   if (err != 0)
555680a9cb8SJohn Marino     return err;
556680a9cb8SJohn Marino   return 0;
557680a9cb8SJohn Marino }
558680a9cb8SJohn Marino 
559680a9cb8SJohn Marino #  else
560680a9cb8SJohn Marino 
561680a9cb8SJohn Marino int
glthread_recursive_lock_init_multithreaded(gl_recursive_lock_t * lock)562680a9cb8SJohn Marino glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
563680a9cb8SJohn Marino {
564680a9cb8SJohn Marino   pthread_mutexattr_t attributes;
565680a9cb8SJohn Marino   int err;
566680a9cb8SJohn Marino 
567680a9cb8SJohn Marino   err = pthread_mutexattr_init (&attributes);
568680a9cb8SJohn Marino   if (err != 0)
569680a9cb8SJohn Marino     return err;
570680a9cb8SJohn Marino   err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
571680a9cb8SJohn Marino   if (err != 0)
572680a9cb8SJohn Marino     {
573680a9cb8SJohn Marino       pthread_mutexattr_destroy (&attributes);
574680a9cb8SJohn Marino       return err;
575680a9cb8SJohn Marino     }
576680a9cb8SJohn Marino   err = pthread_mutex_init (&lock->recmutex, &attributes);
577680a9cb8SJohn Marino   if (err != 0)
578680a9cb8SJohn Marino     {
579680a9cb8SJohn Marino       pthread_mutexattr_destroy (&attributes);
580680a9cb8SJohn Marino       return err;
581680a9cb8SJohn Marino     }
582680a9cb8SJohn Marino   err = pthread_mutexattr_destroy (&attributes);
583680a9cb8SJohn Marino   if (err != 0)
584680a9cb8SJohn Marino     return err;
585680a9cb8SJohn Marino   lock->initialized = 1;
586680a9cb8SJohn Marino   return 0;
587680a9cb8SJohn Marino }
588680a9cb8SJohn Marino 
589680a9cb8SJohn Marino int
glthread_recursive_lock_lock_multithreaded(gl_recursive_lock_t * lock)590680a9cb8SJohn Marino glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
591680a9cb8SJohn Marino {
592680a9cb8SJohn Marino   if (!lock->initialized)
593680a9cb8SJohn Marino     {
594680a9cb8SJohn Marino       int err;
595680a9cb8SJohn Marino 
596680a9cb8SJohn Marino       err = pthread_mutex_lock (&lock->guard);
597680a9cb8SJohn Marino       if (err != 0)
598680a9cb8SJohn Marino         return err;
599680a9cb8SJohn Marino       if (!lock->initialized)
600680a9cb8SJohn Marino         {
601680a9cb8SJohn Marino           err = glthread_recursive_lock_init_multithreaded (lock);
602680a9cb8SJohn Marino           if (err != 0)
603680a9cb8SJohn Marino             {
604680a9cb8SJohn Marino               pthread_mutex_unlock (&lock->guard);
605680a9cb8SJohn Marino               return err;
606680a9cb8SJohn Marino             }
607680a9cb8SJohn Marino         }
608680a9cb8SJohn Marino       err = pthread_mutex_unlock (&lock->guard);
609680a9cb8SJohn Marino       if (err != 0)
610680a9cb8SJohn Marino         return err;
611680a9cb8SJohn Marino     }
612680a9cb8SJohn Marino   return pthread_mutex_lock (&lock->recmutex);
613680a9cb8SJohn Marino }
614680a9cb8SJohn Marino 
615680a9cb8SJohn Marino int
glthread_recursive_lock_unlock_multithreaded(gl_recursive_lock_t * lock)616680a9cb8SJohn Marino glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
617680a9cb8SJohn Marino {
618680a9cb8SJohn Marino   if (!lock->initialized)
619680a9cb8SJohn Marino     return EINVAL;
620680a9cb8SJohn Marino   return pthread_mutex_unlock (&lock->recmutex);
621680a9cb8SJohn Marino }
622680a9cb8SJohn Marino 
623680a9cb8SJohn Marino int
glthread_recursive_lock_destroy_multithreaded(gl_recursive_lock_t * lock)624680a9cb8SJohn Marino glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
625680a9cb8SJohn Marino {
626680a9cb8SJohn Marino   int err;
627680a9cb8SJohn Marino 
628680a9cb8SJohn Marino   if (!lock->initialized)
629680a9cb8SJohn Marino     return EINVAL;
630680a9cb8SJohn Marino   err = pthread_mutex_destroy (&lock->recmutex);
631680a9cb8SJohn Marino   if (err != 0)
632680a9cb8SJohn Marino     return err;
633680a9cb8SJohn Marino   lock->initialized = 0;
634680a9cb8SJohn Marino   return 0;
635680a9cb8SJohn Marino }
636680a9cb8SJohn Marino 
637680a9cb8SJohn Marino #  endif
638680a9cb8SJohn Marino 
639680a9cb8SJohn Marino # else
640680a9cb8SJohn Marino 
641680a9cb8SJohn Marino int
glthread_recursive_lock_init_multithreaded(gl_recursive_lock_t * lock)642680a9cb8SJohn Marino glthread_recursive_lock_init_multithreaded (gl_recursive_lock_t *lock)
643680a9cb8SJohn Marino {
644680a9cb8SJohn Marino   int err;
645680a9cb8SJohn Marino 
646680a9cb8SJohn Marino   err = pthread_mutex_init (&lock->mutex, NULL);
647680a9cb8SJohn Marino   if (err != 0)
648680a9cb8SJohn Marino     return err;
649680a9cb8SJohn Marino   lock->owner = (pthread_t) 0;
650680a9cb8SJohn Marino   lock->depth = 0;
651680a9cb8SJohn Marino   return 0;
652680a9cb8SJohn Marino }
653680a9cb8SJohn Marino 
654680a9cb8SJohn Marino int
glthread_recursive_lock_lock_multithreaded(gl_recursive_lock_t * lock)655680a9cb8SJohn Marino glthread_recursive_lock_lock_multithreaded (gl_recursive_lock_t *lock)
656680a9cb8SJohn Marino {
657680a9cb8SJohn Marino   pthread_t self = pthread_self ();
658680a9cb8SJohn Marino   if (lock->owner != self)
659680a9cb8SJohn Marino     {
660680a9cb8SJohn Marino       int err;
661680a9cb8SJohn Marino 
662680a9cb8SJohn Marino       err = pthread_mutex_lock (&lock->mutex);
663680a9cb8SJohn Marino       if (err != 0)
664680a9cb8SJohn Marino         return err;
665680a9cb8SJohn Marino       lock->owner = self;
666680a9cb8SJohn Marino     }
667680a9cb8SJohn Marino   if (++(lock->depth) == 0) /* wraparound? */
668680a9cb8SJohn Marino     {
669680a9cb8SJohn Marino       lock->depth--;
670680a9cb8SJohn Marino       return EAGAIN;
671680a9cb8SJohn Marino     }
672680a9cb8SJohn Marino   return 0;
673680a9cb8SJohn Marino }
674680a9cb8SJohn Marino 
675680a9cb8SJohn Marino int
glthread_recursive_lock_unlock_multithreaded(gl_recursive_lock_t * lock)676680a9cb8SJohn Marino glthread_recursive_lock_unlock_multithreaded (gl_recursive_lock_t *lock)
677680a9cb8SJohn Marino {
678680a9cb8SJohn Marino   if (lock->owner != pthread_self ())
679680a9cb8SJohn Marino     return EPERM;
680680a9cb8SJohn Marino   if (lock->depth == 0)
681680a9cb8SJohn Marino     return EINVAL;
682680a9cb8SJohn Marino   if (--(lock->depth) == 0)
683680a9cb8SJohn Marino     {
684680a9cb8SJohn Marino       lock->owner = (pthread_t) 0;
685680a9cb8SJohn Marino       return pthread_mutex_unlock (&lock->mutex);
686680a9cb8SJohn Marino     }
687680a9cb8SJohn Marino   else
688680a9cb8SJohn Marino     return 0;
689680a9cb8SJohn Marino }
690680a9cb8SJohn Marino 
691680a9cb8SJohn Marino int
glthread_recursive_lock_destroy_multithreaded(gl_recursive_lock_t * lock)692680a9cb8SJohn Marino glthread_recursive_lock_destroy_multithreaded (gl_recursive_lock_t *lock)
693680a9cb8SJohn Marino {
694680a9cb8SJohn Marino   if (lock->owner != (pthread_t) 0)
695680a9cb8SJohn Marino     return EBUSY;
696680a9cb8SJohn Marino   return pthread_mutex_destroy (&lock->mutex);
697680a9cb8SJohn Marino }
698680a9cb8SJohn Marino 
699680a9cb8SJohn Marino # endif
700680a9cb8SJohn Marino 
701680a9cb8SJohn Marino /* -------------------------- gl_once_t datatype -------------------------- */
702680a9cb8SJohn Marino 
703680a9cb8SJohn Marino static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
704680a9cb8SJohn Marino 
705680a9cb8SJohn Marino int
glthread_once_singlethreaded(pthread_once_t * once_control)706680a9cb8SJohn Marino glthread_once_singlethreaded (pthread_once_t *once_control)
707680a9cb8SJohn Marino {
708680a9cb8SJohn Marino   /* We don't know whether pthread_once_t is an integer type, a floating-point
709680a9cb8SJohn Marino      type, a pointer type, or a structure type.  */
710680a9cb8SJohn Marino   char *firstbyte = (char *)once_control;
711680a9cb8SJohn Marino   if (*firstbyte == *(const char *)&fresh_once)
712680a9cb8SJohn Marino     {
713680a9cb8SJohn Marino       /* First time use of once_control.  Invert the first byte.  */
714680a9cb8SJohn Marino       *firstbyte = ~ *(const char *)&fresh_once;
715680a9cb8SJohn Marino       return 1;
716680a9cb8SJohn Marino     }
717680a9cb8SJohn Marino   else
718680a9cb8SJohn Marino     return 0;
719680a9cb8SJohn Marino }
720680a9cb8SJohn Marino 
721680a9cb8SJohn Marino #endif
722680a9cb8SJohn Marino 
723680a9cb8SJohn Marino /* ========================================================================= */
724680a9cb8SJohn Marino 
725680a9cb8SJohn Marino #if USE_WINDOWS_THREADS
726680a9cb8SJohn Marino 
727680a9cb8SJohn Marino #endif
728680a9cb8SJohn Marino 
729680a9cb8SJohn Marino /* ========================================================================= */
730