errno-h: document Haiku errors can’t be -1
[gnulib.git] / lib / glthread / thread.c
blob82fbd263d4b7cec99e2e0a60df0bfadeac2051ff
1 /* Creating and controlling 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.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */
20 #include <config.h>
22 /* Specification. */
23 #include "glthread/thread.h"
25 #include <stdlib.h>
27 /* ========================================================================= */
29 #if USE_ISOC_THREADS
31 struct thrd_with_exitvalue
33 thrd_t volatile tid;
34 void * volatile exitvalue;
37 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
38 'struct thrd_with_exitvalue *' pointer. */
39 static tss_t thrd_with_exitvalue_key;
41 /* Initializes thrd_with_exitvalue_key.
42 This function must only be called once. */
43 static void
44 do_init_thrd_with_exitvalue_key (void)
46 if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
47 abort ();
50 /* Initializes thrd_with_exitvalue_key. */
51 static void
52 init_thrd_with_exitvalue_key (void)
54 static once_flag once = ONCE_FLAG_INIT;
55 call_once (&once, do_init_thrd_with_exitvalue_key);
58 typedef union
60 struct thrd_with_exitvalue t;
61 struct
63 thrd_t tid; /* reserve memory for t.tid */
64 void *(*mainfunc) (void *);
65 void *arg;
66 } a;
68 main_arg_t;
70 static int
71 thrd_main_func (void *pmarg)
73 /* Unpack the object that combines mainfunc and arg. */
74 main_arg_t *main_arg = (main_arg_t *) pmarg;
75 void *(*mainfunc) (void *) = main_arg->a.mainfunc;
76 void *arg = main_arg->a.arg;
78 if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
79 abort ();
81 /* Execute mainfunc, with arg as argument. */
83 void *exitvalue = mainfunc (arg);
84 /* Store the exitvalue, for use by glthread_join(). */
85 main_arg->t.exitvalue = exitvalue;
86 return 0;
90 int
91 glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
93 init_thrd_with_exitvalue_key ();
95 /* Combine mainfunc and arg in a single object.
96 A stack-allocated object does not work, because it would be out of
97 existence when thrd_create returns before thrd_main_func is
98 entered. So, allocate it in the heap. */
99 main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
100 if (main_arg == NULL)
101 return ENOMEM;
102 main_arg->a.mainfunc = mainfunc;
103 main_arg->a.arg = arg;
104 switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
106 case thrd_success:
107 break;
108 case thrd_nomem:
109 free (main_arg);
110 return ENOMEM;
111 default:
112 free (main_arg);
113 return EAGAIN;
115 *threadp = &main_arg->t;
116 return 0;
120 gl_thread_t
121 gl_thread_self (void)
123 init_thrd_with_exitvalue_key ();
125 gl_thread_t thread =
126 (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_key);
127 if (thread == NULL)
129 /* This happens only in threads that have not been created through
130 glthread_create(), such as the main thread. */
131 for (;;)
133 thread =
134 (struct thrd_with_exitvalue *)
135 malloc (sizeof (struct thrd_with_exitvalue));
136 if (thread != NULL)
137 break;
138 /* Memory allocation failed. There is not much we can do. Have to
139 busy-loop, waiting for the availability of memory. */
141 struct timespec ts =
143 .tv_sec = 1,
144 .tv_nsec = 0
146 thrd_sleep (&ts, NULL);
149 thread->tid = thrd_current ();
150 thread->exitvalue = NULL; /* just to be deterministic */
151 if (tss_set (thrd_with_exitvalue_key, thread) != thrd_success)
152 abort ();
154 return thread;
159 glthread_join (gl_thread_t thread, void **return_value_ptr)
161 /* On Solaris 11.4, thrd_join crashes when the second argument we pass is
162 NULL. */
163 int dummy;
165 if (thread == gl_thread_self ())
166 return EINVAL;
167 if (thrd_join (thread->tid, &dummy) != thrd_success)
168 return EINVAL;
169 if (return_value_ptr != NULL)
170 *return_value_ptr = thread->exitvalue;
171 free (thread);
172 return 0;
175 _Noreturn void
176 gl_thread_exit (void *return_value)
178 gl_thread_t thread = gl_thread_self ();
179 thread->exitvalue = return_value;
180 thrd_exit (0);
183 #endif
185 /* ========================================================================= */
187 #if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
189 #include <pthread.h>
191 #if defined PTW32_VERSION || defined __MVS__
193 const gl_thread_t gl_null_thread /* = { .p = NULL } */;
195 #endif
197 #endif
199 /* ========================================================================= */
201 #if USE_WINDOWS_THREADS
203 #endif
205 /* ========================================================================= */
207 gl_thread_t
208 gl_thread_create (void *(*func) (void *arg), void *arg)
210 gl_thread_t thread;
211 int ret;
213 ret = glthread_create (&thread, func, arg);
214 if (ret != 0)
215 abort ();
216 return thread;