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