1 /* Creating and controlling threads.
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, gthr-win32.h. */
19
20 #include <config.h>
21
22 /* Specification. */
23 #include "glthread/thread.h"
24
25 #include <stdlib.h>
26 #include "glthread/lock.h"
27
28 /* ========================================================================= */
29
30 #if USE_ISOC_THREADS
31
32 struct thrd_with_exitvalue
33 {
34 thrd_t volatile tid;
35 void * volatile exitvalue;
36 };
37
38 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
39 'struct thrd_with_exitvalue *' pointer. */
40 static tss_t thrd_with_exitvalue_key;
41
42 /* Initializes thrd_with_exitvalue_key.
43 This function must only be called once. */
44 static void
do_init_thrd_with_exitvalue_key(void)45 do_init_thrd_with_exitvalue_key (void)
46 {
47 if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
48 abort ();
49 }
50
51 /* Initializes thrd_with_exitvalue_key. */
52 static void
init_thrd_with_exitvalue_key(void)53 init_thrd_with_exitvalue_key (void)
54 {
55 static once_flag once = ONCE_FLAG_INIT;
56 call_once (&once, do_init_thrd_with_exitvalue_key);
57 }
58
59 typedef union
60 {
61 struct thrd_with_exitvalue t;
62 struct
63 {
64 thrd_t tid; /* reserve memory for t.tid */
65 void *(*mainfunc) (void *);
66 void *arg;
67 } a;
68 }
69 main_arg_t;
70
71 static int
thrd_main_func(void * pmarg)72 thrd_main_func (void *pmarg)
73 {
74 /* Unpack the object that combines mainfunc and arg. */
75 main_arg_t *main_arg = (main_arg_t *) pmarg;
76 void *(*mainfunc) (void *) = main_arg->a.mainfunc;
77 void *arg = main_arg->a.arg;
78
79 if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
80 abort ();
81
82 /* Execute mainfunc, with arg as argument. */
83 {
84 void *exitvalue = mainfunc (arg);
85 /* Store the exitvalue, for use by glthread_join(). */
86 main_arg->t.exitvalue = exitvalue;
87 return 0;
88 }
89 }
90
91 int
glthread_create(gl_thread_t * threadp,void * (* mainfunc)(void *),void * arg)92 glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
93 {
94 init_thrd_with_exitvalue_key ();
95 {
96 /* Combine mainfunc and arg in a single object.
97 A stack-allocated object does not work, because it would be out of
98 existence when thrd_create returns before thrd_main_func is
99 entered. So, allocate it in the heap. */
100 main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
101 if (main_arg == NULL)
102 return ENOMEM;
103 main_arg->a.mainfunc = mainfunc;
104 main_arg->a.arg = arg;
105 switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
106 {
107 case thrd_success:
108 break;
109 case thrd_nomem:
110 free (main_arg);
111 return ENOMEM;
112 default:
113 free (main_arg);
114 return EAGAIN;
115 }
116 *threadp = &main_arg->t;
117 return 0;
118 }
119 }
120
121 gl_thread_t
gl_thread_self(void)122 gl_thread_self (void)
123 {
124 init_thrd_with_exitvalue_key ();
125 {
126 gl_thread_t thread =
127 (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_key);
128 if (thread == NULL)
129 {
130 /* This happens only in threads that have not been created through
131 glthread_create(), such as the main thread. */
132 for (;;)
133 {
134 thread =
135 (struct thrd_with_exitvalue *)
136 malloc (sizeof (struct thrd_with_exitvalue));
137 if (thread != NULL)
138 break;
139 /* Memory allocation failed. There is not much we can do. Have to
140 busy-loop, waiting for the availability of memory. */
141 {
142 struct timespec ts;
143 ts.tv_sec = 1;
144 ts.tv_nsec = 0;
145 thrd_sleep (&ts, NULL);
146 }
147 }
148 thread->tid = thrd_current ();
149 thread->exitvalue = NULL; /* just to be deterministic */
150 if (tss_set (thrd_with_exitvalue_key, thread) != thrd_success)
151 abort ();
152 }
153 return thread;
154 }
155 }
156
157 int
glthread_join(gl_thread_t thread,void ** return_value_ptr)158 glthread_join (gl_thread_t thread, void **return_value_ptr)
159 {
160 /* On Solaris 11.4, thrd_join crashes when the second argument we pass is
161 NULL. */
162 int dummy;
163
164 if (thread == gl_thread_self ())
165 return EINVAL;
166 if (thrd_join (thread->tid, &dummy) != thrd_success)
167 return EINVAL;
168 if (return_value_ptr != NULL)
169 *return_value_ptr = thread->exitvalue;
170 free (thread);
171 return 0;
172 }
173
174 _Noreturn void
gl_thread_exit(void * return_value)175 gl_thread_exit (void *return_value)
176 {
177 gl_thread_t thread = gl_thread_self ();
178 thread->exitvalue = return_value;
179 thrd_exit (0);
180 }
181
182 #endif
183
184 /* ========================================================================= */
185
186 #if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
187
188 #include <pthread.h>
189
190 #if defined PTW32_VERSION || defined __MVS__
191
192 const gl_thread_t gl_null_thread /* = { .p = NULL } */;
193
194 #endif
195
196 #endif
197
198 /* ========================================================================= */
199
200 #if USE_WINDOWS_THREADS
201
202 #endif
203
204 /* ========================================================================= */
205
206 gl_thread_t
gl_thread_create(void * (* func)(void * arg),void * arg)207 gl_thread_create (void *(*func) (void *arg), void *arg)
208 {
209 gl_thread_t thread;
210 int ret;
211
212 ret = glthread_create (&thread, func, arg);
213 if (ret != 0)
214 abort ();
215 return thread;
216 }
217