]> git.draconx.ca Git - cdecl99.git/blob - src/thread-posix.h
Release 1.3.
[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 #  pragma weak pthread_once
60 #endif
61
62 static struct {
63         union {
64                 pthread_key_t mt;
65 #if USE_POSIX_THREADS_WEAK
66                 void *st;
67 #endif
68         } u;
69
70 #if USE_POSIX_THREADS_WEAK
71         signed char valid;
72 #else
73         bool valid;
74 #endif
75 } tls_key;
76 #define tls_key_valid (tls_key.valid != 0)
77
78 static void init_once_cb(void);
79
80 static void *tls_get(void)
81 {
82 #if USE_POSIX_THREADS_WEAK
83         if (tls_key.valid < 0)
84                 return tls_key.u.st;
85 #endif
86         return pthread_getspecific(tls_key.u.mt);
87 }
88
89 static int tls_set(void *p)
90 {
91 #if USE_POSIX_THREADS_WEAK
92         if (tls_key.valid < 0)
93                 return (tls_key.u.st = p, 1);
94 #endif
95         return !pthread_setspecific(tls_key.u.mt, p);
96 }
97
98 static void init_once_with_tls(void)
99 {
100         tls_key.valid = !pthread_key_create(&tls_key.u.mt, free);
101         init_once_cb();
102 }
103
104 static int init_once(void)
105 {
106         if (pthread_in_use) {
107                 static pthread_once_t ctrl = PTHREAD_ONCE_INIT;
108                 return !pthread_once(&ctrl, init_once_with_tls);
109         } else if (!tls_key_valid) {
110                 tls_key.valid = -1;
111                 init_once_cb();
112         }
113
114         return 1;
115 }