]> git.draconx.ca Git - cdecl99.git/blob - src/thread-posix.h
libcdecl: Avoid asserts in output declarator traversal.
[cdecl99.git] / src / thread-posix.h
1 /*
2  * Copyright © 2024 Nick Bowler
3  *
4  * Helpers for hosts using POSIX threading (pthreads) API.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * The pthread_in_use detection is based on Gnulib code written by Bruno
12  * Haible, distributed with the following copyright and permission notice:
13  *
14  *      Copyright (C) 2005-2022 Free Software Foundation, Inc.
15  *
16  *      This file is free software: you can redistribute it and/or modify
17  *      it under the terms of the GNU Lesser General Public License as
18  *      published by the Free Software Foundation; either version 2.1 of
19  *      the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
28  */
29
30 #include <pthread.h>
31
32 extern int glthread_in_use();
33
34 #if !defined c11_threads_in_use
35 #  if HAVE_THREADS_H && USE_POSIX_THREADS_FROM_LIBC
36 #    define c11_threads_in_use 1
37 #  elif HAVE_THREADS_H && USE_POSIX_THREADS_WEAK
38 #    include <threads.h>
39 #    pragma weak thrd_exit
40 #    define c11_threads_in_use (thrd_exit != NULL)
41 #  else
42 #    define c11_threads_in_use 0
43 #  endif
44 #endif
45
46 #if PTHREAD_IN_USE_DETECTION_HARD
47 #  define pthread_in_use glthread_in_use()
48 #elif USE_POSIX_THREADS_WEAK
49 #  pragma weak pthread_mutexattr_gettype
50 #  define pthread_in_use (pthread_mutexattr_gettype || c11_threads_in_use)
51 #else
52 #  define pthread_in_use 1
53 #endif
54
55 #if USE_POSIX_THREADS_WEAK
56 #  pragma weak pthread_key_create
57 #  pragma weak pthread_getspecific
58 #  pragma weak pthread_setspecific
59 #endif
60
61 static struct {
62         union {
63                 pthread_key_t mt;
64 #if USE_POSIX_THREADS_WEAK
65                 void *st;
66 #endif
67         } u;
68
69 #if USE_POSIX_THREADS_WEAK
70         signed char valid;
71 #else
72         bool valid;
73 #endif
74 } tls_key;
75 #define tls_key_valid (tls_key.valid != 0)
76
77 static void init_once_cb(void);
78
79 static void *tls_get(void)
80 {
81 #if USE_POSIX_THREADS_WEAK
82         if (tls_key.valid < 0)
83                 return tls_key.u.st;
84 #endif
85         return pthread_getspecific(tls_key.u.mt);
86 }
87
88 static int tls_set(void *p)
89 {
90 #if USE_POSIX_THREADS_WEAK
91         if (tls_key.valid < 0)
92                 return (tls_key.u.st = p, 1);
93 #endif
94         return !pthread_setspecific(tls_key.u.mt, p);
95 }
96
97 static void init_once_with_tls(void)
98 {
99         tls_key.valid = !pthread_key_create(&tls_key.u.mt, free);
100         init_once_cb();
101 }
102
103 static int init_once(void)
104 {
105         if (pthread_in_use) {
106                 static pthread_once_t ctrl = PTHREAD_ONCE_INIT;
107                 return !pthread_once(&ctrl, init_once_with_tls);
108         } else if (!tls_key_valid) {
109                 tls_key.valid = -1;
110                 init_once_cb();
111         }
112
113         return 1;
114 }