1 /* Creating and controlling ISO C 11 threads.
2 Copyright (C) 2005-2025 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. */
27 /* Provide workarounds. */
29 # if BROKEN_THRD_START_T_OR_JOIN
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. */
46 do_init_thrd_with_exitcode_key (void)
48 if (tss_create (&thrd_with_exitcode_key
, NULL
) != thrd_success
)
52 /* Initializes thrd_with_exitcode_key. */
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
);
62 struct thrd_with_exitcode t
;
65 thrd_t tid
; /* reserve memory for t.tid */
66 int detached
; /* reserve memory for t.detached */
67 thrd_start_t mainfunc
;
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
)
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. */
99 rpl_thrd_create (rpl_thrd_t
*threadp
, thrd_start_t mainfunc
, void *arg
)
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
)
111 main_arg
->a
.mainfunc
= mainfunc
;
112 main_arg
->a
.arg
= arg
;
113 main_arg
->t
.detached
= 0;
116 thrd_create ((thrd_t
*) &main_arg
->t
.tid
, thrd_main_func
, main_arg
);
117 if (err
== thrd_success
)
118 *threadp
= &main_arg
->t
;
127 rpl_thrd_current (void)
130 init_thrd_with_exitcode_key ();
133 (struct thrd_with_exitcode
*) tss_get (thrd_with_exitcode_key
);
136 /* This happens only in threads that have not been created through
137 thrd_create(), such as the main thread. */
141 (struct thrd_with_exitcode
*)
142 malloc (sizeof (struct thrd_with_exitcode
));
145 /* Memory allocation failed. There is not much we can do. Have to
146 busy-loop, waiting for the availability of memory. */
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
)
167 rpl_thrd_equal (rpl_thrd_t thread1
, rpl_thrd_t thread2
)
169 return thread1
== thread2
;
173 rpl_thrd_detach (rpl_thrd_t thread
)
176 if (thread
->detached
)
180 thrd_detach (thread
== rpl_thrd_current ()
181 ? /* thread->tid may not be initialized at this point. */
184 if (err
== thrd_success
)
185 thread
->detached
= 1;
191 rpl_thrd_join (rpl_thrd_t thread
, int *exitcodep
)
194 if (thread
== rpl_thrd_current () || thread
->detached
)
197 int err
= thrd_join (thread
->tid
, NULL
);
198 if (err
== thrd_success
)
200 if (exitcodep
!= NULL
)
201 *exitcodep
= thread
->exitcode
;
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
;
217 /* Clean up the thread, like thrd_join would do. */
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
)
233 int err
= thrd_join (thread
, &exitcode
);
234 if (err
== 0 && exitcodep
!= NULL
)
235 *exitcodep
= exitcode
;
246 # if defined _WIN32 && ! defined __CYGWIN__
247 /* Use Windows threads. */
249 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
250 # include <windows.h>
253 /* Use POSIX threads. */
255 # include <pthread.h>
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
;
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
;
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
)
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
);
314 free (pthread_main_arg
);
315 return (err
== 0 ? thrd_success
:
316 err
== ENOMEM
/* || err == EAGAIN */ ? thrd_nomem
:
324 return glwthread_thread_self ();
328 thrd_equal (thrd_t thread1
, thrd_t thread2
)
330 return thread1
== thread2
;
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
)
350 int err
= glwthread_thread_join (thread
, &exitptr
);
353 if (exitcodep
!= NULL
)
354 *exitcodep
= (int) (intptr_t) exitptr
;
362 thrd_exit (int exitcode
)
364 glwthread_thread_exit ((void *) (intptr_t) exitcode
);
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
)
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
);
388 free (pthread_main_arg
);
389 return (err
== 0 ? thrd_success
:
390 err
== ENOMEM
/* || err == EAGAIN */ ? thrd_nomem
:
398 return pthread_self ();
402 thrd_equal (thrd_t thread1
, thrd_t thread2
)
404 return pthread_equal (thread1
, thread2
);
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
)
424 int err
= pthread_join (thread
, &exitptr
);
427 if (exitcodep
!= NULL
)
428 *exitcodep
= (int) (intptr_t) exitptr
;
436 thrd_exit (int exitcode
)
438 pthread_exit ((void *) (intptr_t) exitcode
);
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);