exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / thrd.c
blob8da0bb403a1741b27854ca3966f550b43e12070b
1 /* Creating and controlling ISO C 11 threads.
2 Copyright (C) 2005-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */
20 #include <config.h>
22 #include <threads.h>
24 #include <stdlib.h>
26 #if HAVE_THREADS_H
27 /* Provide workarounds. */
29 # if BROKEN_THRD_START_T_OR_JOIN
31 # undef thrd_t
33 /* AIX 7.1..7.2 defines thrd_start_t incorrectly, namely as
34 'void * (*) (void *)' instead of 'int (*) (void *)'.
35 As a consequence, its thrd_join function never stores an exit code.
36 AIX 7.3.1 has a corrected thrd_start_t. But the thrd_join function still
37 never stores an exit code. */
39 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
40 'struct thrd_with_exitcode *' pointer. */
41 static tss_t thrd_with_exitcode_key;
43 /* Initializes thrd_with_exitcode_key.
44 This function must only be called once. */
45 static void
46 do_init_thrd_with_exitcode_key (void)
48 if (tss_create (&thrd_with_exitcode_key, NULL) != thrd_success)
49 abort ();
52 /* Initializes thrd_with_exitcode_key. */
53 static void
54 init_thrd_with_exitcode_key (void)
56 static once_flag once = ONCE_FLAG_INIT;
57 call_once (&once, do_init_thrd_with_exitcode_key);
60 typedef union
62 struct thrd_with_exitcode t;
63 struct
65 thrd_t tid; /* reserve memory for t.tid */
66 int detached; /* reserve memory for t.detached */
67 thrd_start_t mainfunc;
68 void *arg;
69 } a;
71 main_arg_t;
73 static void *
74 thrd_main_func (void *pmarg)
76 /* Unpack the object that combines mainfunc and arg. */
77 main_arg_t *main_arg = (main_arg_t *) pmarg;
78 thrd_start_t mainfunc = main_arg->a.mainfunc;
79 void *arg = main_arg->a.arg;
81 if (tss_set (thrd_with_exitcode_key, &main_arg->t) != thrd_success)
82 abort ();
84 /* Execute mainfunc, with arg as argument. */
86 int exitcode = mainfunc (arg);
87 /* Store the exitcode, for use by thrd_join(). */
88 main_arg->t.exitcode = exitcode;
89 if (main_arg->t.detached)
91 /* Clean up the thread, like thrd_join would do. */
92 free (&main_arg->t);
94 return NULL;
98 int
99 rpl_thrd_create (rpl_thrd_t *threadp, thrd_start_t mainfunc, void *arg)
100 # undef thrd_create
102 init_thrd_with_exitcode_key ();
104 /* Combine mainfunc and arg in a single object.
105 A stack-allocated object does not work, because it would be out of
106 existence when thrd_create returns before pthread_main_func is
107 entered. So, allocate it in the heap. */
108 main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
109 if (main_arg == NULL)
110 return thrd_nomem;
111 main_arg->a.mainfunc = mainfunc;
112 main_arg->a.arg = arg;
113 main_arg->t.detached = 0;
115 int err =
116 thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg);
117 if (err == thrd_success)
118 *threadp = &main_arg->t;
119 else
120 free (main_arg);
121 return err;
126 rpl_thrd_t
127 rpl_thrd_current (void)
128 # undef thrd_current
130 init_thrd_with_exitcode_key ();
132 rpl_thrd_t thread =
133 (struct thrd_with_exitcode *) tss_get (thrd_with_exitcode_key);
134 if (thread == NULL)
136 /* This happens only in threads that have not been created through
137 thrd_create(), such as the main thread. */
138 for (;;)
140 thread =
141 (struct thrd_with_exitcode *)
142 malloc (sizeof (struct thrd_with_exitcode));
143 if (thread != NULL)
144 break;
145 /* Memory allocation failed. There is not much we can do. Have to
146 busy-loop, waiting for the availability of memory. */
148 struct timespec ts =
150 .tv_sec = 1,
151 .tv_nsec = 0
153 thrd_sleep (&ts, NULL);
156 thread->tid = thrd_current ();
157 thread->detached = 0; /* This can lead to a memory leak. */
158 thread->exitcode = 0; /* just to be deterministic */
159 if (tss_set (thrd_with_exitcode_key, thread) != thrd_success)
160 abort ();
162 return thread;
167 rpl_thrd_equal (rpl_thrd_t thread1, rpl_thrd_t thread2)
169 return thread1 == thread2;
173 rpl_thrd_detach (rpl_thrd_t thread)
174 # undef thrd_detach
176 if (thread->detached)
177 return thrd_error;
179 int err =
180 thrd_detach (thread == rpl_thrd_current ()
181 ? /* thread->tid may not be initialized at this point. */
182 thrd_current ()
183 : thread->tid);
184 if (err == thrd_success)
185 thread->detached = 1;
186 return err;
191 rpl_thrd_join (rpl_thrd_t thread, int *exitcodep)
192 # undef thrd_join
194 if (thread == rpl_thrd_current () || thread->detached)
195 return thrd_error;
197 int err = thrd_join (thread->tid, NULL);
198 if (err == thrd_success)
200 if (exitcodep != NULL)
201 *exitcodep = thread->exitcode;
202 free (thread);
204 return err;
208 _Noreturn void
209 rpl_thrd_exit (int exitcode)
211 rpl_thrd_t t = rpl_thrd_current ();
213 /* Store the exitcode, for use by thrd_join(). */
214 t->exitcode = exitcode;
215 if (t->detached)
217 /* Clean up the thread, like thrd_join would do. */
218 free (t);
220 pthread_exit (NULL);
223 # endif
225 # if BROKEN_THRD_JOIN_NULL
227 /* On Solaris 11.4, thrd_join crashes when the second argument is NULL. */
229 rpl_thrd_join (thrd_t thread, int *exitcodep)
230 # undef thrd_join
232 int exitcode;
233 int err = thrd_join (thread, &exitcode);
234 if (err == 0 && exitcodep != NULL)
235 *exitcodep = exitcode;
236 return err;
239 # endif
241 #else
243 # include <errno.h>
244 # include <stdint.h>
246 # if defined _WIN32 && ! defined __CYGWIN__
247 /* Use Windows threads. */
249 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
250 # include <windows.h>
252 # else
253 /* Use POSIX threads. */
255 # include <pthread.h>
256 # include <sched.h>
258 # endif
260 /* The main functions passed to thrd_create and
261 pthread_create/glwthread_thread_create have different return types:
262 'int' vs. 'void *'. */
264 struct pthread_main_arg_t
266 thrd_start_t mainfunc;
267 void *arg;
270 static void *
271 pthread_main_func (void *pmarg)
273 /* Unpack the object that combines mainfunc and arg. */
274 struct pthread_main_arg_t *pthread_main_arg =
275 (struct pthread_main_arg_t *) pmarg;
276 thrd_start_t mainfunc = pthread_main_arg->mainfunc;
277 void *arg = pthread_main_arg->arg;
279 /* Free it. */
280 free (pmarg);
282 /* Execute mainfunc, with arg as argument. */
284 int exitcode = mainfunc (arg);
285 /* Note: When using Windows threads, this exit code is different from the
286 argument passed to ExitThread(), because the latter should never be 259,
287 see <https://docs.microsoft.com/de-de/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodethread>,
288 whereas the exit code passed to thrd_exit() is not constrained. */
289 return (void *) (intptr_t) exitcode;
293 # if defined _WIN32 && ! defined __CYGWIN__
294 /* Use Windows threads. */
297 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
299 /* Combine mainfunc and arg in a single object.
300 A stack-allocated object does not work, because it would be out of
301 existence when thrd_create returns before pthread_main_func is
302 entered. So, allocate it in the heap. */
303 struct pthread_main_arg_t *pthread_main_arg =
304 (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
305 if (pthread_main_arg == NULL)
306 return thrd_nomem;
307 pthread_main_arg->mainfunc = mainfunc;
308 pthread_main_arg->arg = arg;
311 int err = glwthread_thread_create (threadp, 0,
312 pthread_main_func, pthread_main_arg);
313 if (err != 0)
314 free (pthread_main_arg);
315 return (err == 0 ? thrd_success :
316 err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
317 thrd_error);
321 thrd_t
322 thrd_current (void)
324 return glwthread_thread_self ();
328 thrd_equal (thrd_t thread1, thrd_t thread2)
330 return thread1 == thread2;
333 void
334 thrd_yield (void)
336 Sleep (0);
340 thrd_detach (thrd_t thread)
342 int err = glwthread_thread_detach (thread);
343 return (err == 0 ? thrd_success : thrd_error);
347 thrd_join (thrd_t thread, int *exitcodep)
349 void *exitptr;
350 int err = glwthread_thread_join (thread, &exitptr);
351 if (err == 0)
353 if (exitcodep != NULL)
354 *exitcodep = (int) (intptr_t) exitptr;
355 return thrd_success;
357 else
358 return thrd_error;
361 _Noreturn void
362 thrd_exit (int exitcode)
364 glwthread_thread_exit ((void *) (intptr_t) exitcode);
367 # else
368 /* Use POSIX threads. */
371 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
373 /* Combine mainfunc and arg in a single object.
374 A stack-allocated object does not work, because it would be out of
375 existence when thrd_create returns before pthread_main_func is
376 entered. So, allocate it in the heap. */
377 struct pthread_main_arg_t *pthread_main_arg =
378 (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
379 if (pthread_main_arg == NULL)
380 return thrd_nomem;
381 pthread_main_arg->mainfunc = mainfunc;
382 pthread_main_arg->arg = arg;
385 int err = pthread_create (threadp, NULL,
386 pthread_main_func, pthread_main_arg);
387 if (err != 0)
388 free (pthread_main_arg);
389 return (err == 0 ? thrd_success :
390 err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
391 thrd_error);
395 thrd_t
396 thrd_current (void)
398 return pthread_self ();
402 thrd_equal (thrd_t thread1, thrd_t thread2)
404 return pthread_equal (thread1, thread2);
407 void
408 thrd_yield (void)
410 sched_yield ();
414 thrd_detach (thrd_t thread)
416 int err = pthread_detach (thread);
417 return (err == 0 ? thrd_success : thrd_error);
421 thrd_join (thrd_t thread, int *exitcodep)
423 void *exitptr;
424 int err = pthread_join (thread, &exitptr);
425 if (err == 0)
427 if (exitcodep != NULL)
428 *exitcodep = (int) (intptr_t) exitptr;
429 return thrd_success;
431 else
432 return thrd_error;
435 _Noreturn void
436 thrd_exit (int exitcode)
438 pthread_exit ((void *) (intptr_t) exitcode);
441 # endif
444 thrd_sleep (const struct timespec *duration, struct timespec *remaining)
446 int ret = nanosleep (duration, remaining);
447 return (ret == 0 ? 0 : errno == EINTR ? -1 : -2);
450 #endif