Avoid "warning: The macro `AC_DECL_SYS_SIGLIST' is obsolete".
[gnulib.git] / lib / glthread / thread.c
blob38d19a22484729516baa085fa1fa03a2a888d2e1
1 /* Creating and controlling 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.
18 Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */
20 #include <config.h>
22 /* Specification. */
23 # define _GLTHREAD_THREAD_INLINE _GL_EXTERN_INLINE
24 #include "glthread/thread.h"
26 #include <stdlib.h>
27 #include "glthread/lock.h"
29 /* ========================================================================= */
31 #if USE_ISOC_THREADS
33 struct thrd_with_exitvalue
35 thrd_t volatile tid;
36 void * volatile exitvalue;
39 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
40 'struct thrd_with_exitvalue *' pointer. */
41 static tss_t thrd_with_exitvalue_key;
43 /* Initializes thrd_with_exitvalue_key.
44 This function must only be called once. */
45 static void
46 do_init_thrd_with_exitvalue_key (void)
48 if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
49 abort ();
52 /* Initializes thrd_with_exitvalue_key. */
53 static void
54 init_thrd_with_exitvalue_key (void)
56 static once_flag once = ONCE_FLAG_INIT;
57 call_once (&once, do_init_thrd_with_exitvalue_key);
60 typedef union
62 struct thrd_with_exitvalue t;
63 struct
65 thrd_t tid; /* reserve memory for t.tid */
66 void *(*mainfunc) (void *);
67 void *arg;
68 } a;
70 main_arg_t;
72 static int
73 thrd_main_func (void *pmarg)
75 /* Unpack the object that combines mainfunc and arg. */
76 main_arg_t *main_arg = (main_arg_t *) pmarg;
77 void *(*mainfunc) (void *) = main_arg->a.mainfunc;
78 void *arg = main_arg->a.arg;
80 if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
81 abort ();
83 /* Execute mainfunc, with arg as argument. */
85 void *exitvalue = mainfunc (arg);
86 /* Store the exitvalue, for use by glthread_join(). */
87 main_arg->t.exitvalue = exitvalue;
88 return 0;
92 int
93 glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
95 init_thrd_with_exitvalue_key ();
97 /* Combine mainfunc and arg in a single object.
98 A stack-allocated object does not work, because it would be out of
99 existence when thrd_create returns before thrd_main_func is
100 entered. So, allocate it in the heap. */
101 main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
102 if (main_arg == NULL)
103 return ENOMEM;
104 main_arg->a.mainfunc = mainfunc;
105 main_arg->a.arg = arg;
106 switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
108 case thrd_success:
109 break;
110 case thrd_nomem:
111 free (main_arg);
112 return ENOMEM;
113 default:
114 free (main_arg);
115 return EAGAIN;
117 *threadp = &main_arg->t;
118 return 0;
122 gl_thread_t
123 gl_thread_self (void)
125 init_thrd_with_exitvalue_key ();
127 gl_thread_t thread =
128 (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_key);
129 if (thread == NULL)
131 /* This happens only in threads that have not been created through
132 glthread_create(), such as the main thread. */
133 for (;;)
135 thread =
136 (struct thrd_with_exitvalue *)
137 malloc (sizeof (struct thrd_with_exitvalue));
138 if (thread != NULL)
139 break;
140 /* Memory allocation failed. There is not much we can do. Have to
141 busy-loop, waiting for the availability of memory. */
143 struct timespec ts;
144 ts.tv_sec = 1;
145 ts.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 /* ========================================================================= */