havelib: Fix for Solaris 11 OpenIndiana and Solaris 11 OmniOS.
[gnulib.git] / lib / thrd.c
blob88c08012e311fa036dc2b4c72609c6a579e7aef2
1 /* Creating and controlling ISO C 11 threads.
2 Copyright (C) 2005-2020 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program 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 General Public License for more details.
14 You should have received a copy of the GNU 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
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. */
37 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
38 'struct thrd_with_exitcode *' pointer. */
39 static tss_t thrd_with_exitcode_key;
41 /* Initializes thrd_with_exitcode_key.
42 This function must only be called once. */
43 static void
44 do_init_thrd_with_exitcode_key (void)
46 if (tss_create (&thrd_with_exitcode_key, NULL) != thrd_success)
47 abort ();
50 /* Initializes thrd_with_exitcode_key. */
51 static void
52 init_thrd_with_exitcode_key (void)
54 static once_flag once = ONCE_FLAG_INIT;
55 call_once (&once, do_init_thrd_with_exitcode_key);
58 typedef union
60 struct thrd_with_exitcode t;
61 struct
63 thrd_t tid; /* reserve memory for t.tid */
64 int detached; /* reserve memory for t.detached */
65 thrd_start_t mainfunc;
66 void *arg;
67 } a;
69 main_arg_t;
71 static void *
72 thrd_main_func (void *pmarg)
74 /* Unpack the object that combines mainfunc and arg. */
75 main_arg_t *main_arg = (main_arg_t *) pmarg;
76 thrd_start_t mainfunc = main_arg->a.mainfunc;
77 void *arg = main_arg->a.arg;
79 if (tss_set (thrd_with_exitcode_key, &main_arg->t) != thrd_success)
80 abort ();
82 /* Execute mainfunc, with arg as argument. */
84 int exitcode = mainfunc (arg);
85 /* Store the exitcode, for use by thrd_join(). */
86 main_arg->t.exitcode = exitcode;
87 if (main_arg->t.detached)
89 /* Clean up the thread, like thrd_join would do. */
90 free (&main_arg->t);
92 return NULL;
96 int
97 rpl_thrd_create (rpl_thrd_t *threadp, thrd_start_t mainfunc, void *arg)
98 # undef thrd_create
100 init_thrd_with_exitcode_key ();
102 /* Combine mainfunc and arg in a single object.
103 A stack-allocated object does not work, because it would be out of
104 existence when thrd_create returns before pthread_main_func is
105 entered. So, allocate it in the heap. */
106 main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
107 if (main_arg == NULL)
108 return thrd_nomem;
109 main_arg->a.mainfunc = mainfunc;
110 main_arg->a.arg = arg;
111 main_arg->t.detached = 0;
113 int err =
114 thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg);
115 if (err == thrd_success)
116 *threadp = &main_arg->t;
117 else
118 free (main_arg);
119 return err;
124 rpl_thrd_t
125 rpl_thrd_current (void)
126 # undef thrd_current
128 init_thrd_with_exitcode_key ();
130 rpl_thrd_t thread =
131 (struct thrd_with_exitcode *) tss_get (thrd_with_exitcode_key);
132 if (thread == NULL)
134 /* This happens only in threads that have not been created through
135 thrd_create(), such as the main thread. */
136 for (;;)
138 thread =
139 (struct thrd_with_exitcode *)
140 malloc (sizeof (struct thrd_with_exitcode));
141 if (thread != NULL)
142 break;
143 /* Memory allocation failed. There is not much we can do. Have to
144 busy-loop, waiting for the availability of memory. */
146 struct timespec ts;
147 ts.tv_sec = 1;
148 ts.tv_nsec = 0;
149 thrd_sleep (&ts, NULL);
152 thread->tid = thrd_current ();
153 thread->detached = 0; /* This can lead to a memory leak. */
154 thread->exitcode = 0; /* just to be deterministic */
155 if (tss_set (thrd_with_exitcode_key, thread) != thrd_success)
156 abort ();
158 return thread;
163 rpl_thrd_equal (rpl_thrd_t thread1, rpl_thrd_t thread2)
165 return thread1 == thread2;
169 rpl_thrd_detach (rpl_thrd_t thread)
170 # undef thrd_detach
172 if (thread->detached)
173 return thrd_error;
175 int err =
176 thrd_detach (thread == rpl_thrd_current ()
177 ? /* thread->tid may not be initialized at this point. */
178 thrd_current ()
179 : thread->tid);
180 if (err == thrd_success)
181 thread->detached = 1;
182 return err;
187 rpl_thrd_join (rpl_thrd_t thread, int *exitcodep)
188 # undef thrd_join
190 if (thread == rpl_thrd_current () || thread->detached)
191 return thrd_error;
193 int err = thrd_join (thread->tid, NULL);
194 if (err == thrd_success)
196 if (exitcodep != NULL)
197 *exitcodep = thread->exitcode;
198 free (thread);
200 return err;
204 # endif
206 # if BROKEN_THRD_JOIN
208 /* On Solaris 11.4, thrd_join crashes when the second argument is NULL. */
210 rpl_thrd_join (thrd_t thread, int *exitcodep)
211 # undef thrd_join
213 int exitcode;
214 int err = thrd_join (thread, &exitcode);
215 if (err == 0 && exitcodep != NULL)
216 *exitcodep = exitcode;
217 return err;
220 # endif
222 #else
224 # include <errno.h>
225 # include <stdint.h>
227 # if defined _WIN32 && ! defined __CYGWIN__
228 /* Use Windows threads. */
230 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
231 # include <windows.h>
233 # else
234 /* Use POSIX threads. */
236 # include <pthread.h>
237 # include <sched.h>
239 # endif
241 /* The main functions passed to thrd_create and
242 pthread_create/glwthread_thread_create have different return types:
243 'int' vs. 'void *'. */
245 struct pthread_main_arg_t
247 thrd_start_t mainfunc;
248 void *arg;
251 static void *
252 pthread_main_func (void *pmarg)
254 /* Unpack the object that combines mainfunc and arg. */
255 struct pthread_main_arg_t *pthread_main_arg =
256 (struct pthread_main_arg_t *) pmarg;
257 thrd_start_t mainfunc = pthread_main_arg->mainfunc;
258 void *arg = pthread_main_arg->arg;
260 /* Free it. */
261 free (pmarg);
263 /* Execute mainfunc, with arg as argument. */
265 int exitcode = mainfunc (arg);
266 /* Note: When using Windows threads, this exit code is different from the
267 argument passed to ExitThread(), because the latter should never be 259,
268 see <https://docs.microsoft.com/de-de/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodethread>,
269 whereas the exit code passed to thrd_exit() is not constrained. */
270 return (void *) (intptr_t) exitcode;
274 # if defined _WIN32 && ! defined __CYGWIN__
275 /* Use Windows threads. */
278 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
280 /* Combine mainfunc and arg in a single object.
281 A stack-allocated object does not work, because it would be out of
282 existence when thrd_create returns before pthread_main_func is
283 entered. So, allocate it in the heap. */
284 struct pthread_main_arg_t *pthread_main_arg =
285 (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
286 if (pthread_main_arg == NULL)
287 return thrd_nomem;
288 pthread_main_arg->mainfunc = mainfunc;
289 pthread_main_arg->arg = arg;
292 int err = glwthread_thread_create (threadp, 0,
293 pthread_main_func, pthread_main_arg);
294 if (err != 0)
295 free (pthread_main_arg);
296 return (err == 0 ? thrd_success :
297 err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
298 thrd_error);
302 thrd_t
303 thrd_current (void)
305 return glwthread_thread_self ();
309 thrd_equal (thrd_t thread1, thrd_t thread2)
311 return thread1 == thread2;
314 void
315 thrd_yield (void)
317 Sleep (0);
321 thrd_detach (thrd_t thread)
323 int err = glwthread_thread_detach (thread);
324 return (err == 0 ? thrd_success : thrd_error);
328 thrd_join (thrd_t thread, int *exitcodep)
330 void *exitptr;
331 int err = glwthread_thread_join (thread, &exitptr);
332 if (err == 0)
334 if (exitcodep != NULL)
335 *exitcodep = (int) (intptr_t) exitptr;
336 return thrd_success;
338 else
339 return thrd_error;
342 _Noreturn void
343 thrd_exit (int exitcode)
345 glwthread_thread_exit ((void *) (intptr_t) exitcode);
348 # else
349 /* Use POSIX threads. */
352 thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
354 /* Combine mainfunc and arg in a single object.
355 A stack-allocated object does not work, because it would be out of
356 existence when thrd_create returns before pthread_main_func is
357 entered. So, allocate it in the heap. */
358 struct pthread_main_arg_t *pthread_main_arg =
359 (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
360 if (pthread_main_arg == NULL)
361 return thrd_nomem;
362 pthread_main_arg->mainfunc = mainfunc;
363 pthread_main_arg->arg = arg;
366 int err = pthread_create (threadp, NULL,
367 pthread_main_func, pthread_main_arg);
368 if (err != 0)
369 free (pthread_main_arg);
370 return (err == 0 ? thrd_success :
371 err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
372 thrd_error);
376 thrd_t
377 thrd_current (void)
379 return pthread_self ();
383 thrd_equal (thrd_t thread1, thrd_t thread2)
385 return pthread_equal (thread1, thread2);
388 void
389 thrd_yield (void)
391 sched_yield ();
395 thrd_detach (thrd_t thread)
397 int err = pthread_detach (thread);
398 return (err == 0 ? thrd_success : thrd_error);
402 thrd_join (thrd_t thread, int *exitcodep)
404 void *exitptr;
405 int err = pthread_join (thread, &exitptr);
406 if (err == 0)
408 if (exitcodep != NULL)
409 *exitcodep = (int) (intptr_t) exitptr;
410 return thrd_success;
412 else
413 return thrd_error;
416 _Noreturn void
417 thrd_exit (int exitcode)
419 pthread_exit ((void *) (intptr_t) exitcode);
422 # endif
425 thrd_sleep (const struct timespec *duration, struct timespec *remaining)
427 int ret = nanosleep (duration, remaining);
428 return (ret == 0 ? 0 : errno == EINTR ? -1 : -2);
431 #endif