1 /* Creating and controlling threads.
2    Copyright (C) 2005-2021 Free Software Foundation, Inc.
3 
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.
8 
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.
13 
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/>.  */
16 
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18    Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */
19 
20 #include <config.h>
21 
22 /* Specification.  */
23 #include "glthread/thread.h"
24 
25 #include <stdlib.h>
26 #include "glthread/lock.h"
27 
28 /* ========================================================================= */
29 
30 #if USE_ISOC_THREADS
31 
32 struct thrd_with_exitvalue
33 {
34   thrd_t volatile tid;
35   void * volatile exitvalue;
36 };
37 
38 /* The Thread-Specific Storage (TSS) key that allows to access each thread's
39    'struct thrd_with_exitvalue *' pointer.  */
40 static tss_t thrd_with_exitvalue_key;
41 
42 /* Initializes thrd_with_exitvalue_key.
43    This function must only be called once.  */
44 static void
do_init_thrd_with_exitvalue_key(void)45 do_init_thrd_with_exitvalue_key (void)
46 {
47   if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
48     abort ();
49 }
50 
51 /* Initializes thrd_with_exitvalue_key.  */
52 static void
init_thrd_with_exitvalue_key(void)53 init_thrd_with_exitvalue_key (void)
54 {
55   static once_flag once = ONCE_FLAG_INIT;
56   call_once (&once, do_init_thrd_with_exitvalue_key);
57 }
58 
59 typedef union
60         {
61           struct thrd_with_exitvalue t;
62           struct
63           {
64             thrd_t tid; /* reserve memory for t.tid */
65             void *(*mainfunc) (void *);
66             void *arg;
67           } a;
68         }
69         main_arg_t;
70 
71 static int
thrd_main_func(void * pmarg)72 thrd_main_func (void *pmarg)
73 {
74   /* Unpack the object that combines mainfunc and arg.  */
75   main_arg_t *main_arg = (main_arg_t *) pmarg;
76   void *(*mainfunc) (void *) = main_arg->a.mainfunc;
77   void *arg = main_arg->a.arg;
78 
79   if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
80     abort ();
81 
82   /* Execute mainfunc, with arg as argument.  */
83   {
84     void *exitvalue = mainfunc (arg);
85     /* Store the exitvalue, for use by glthread_join().  */
86     main_arg->t.exitvalue = exitvalue;
87     return 0;
88   }
89 }
90 
91 int
glthread_create(gl_thread_t * threadp,void * (* mainfunc)(void *),void * arg)92 glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
93 {
94   init_thrd_with_exitvalue_key ();
95   {
96     /* Combine mainfunc and arg in a single object.
97        A stack-allocated object does not work, because it would be out of
98        existence when thrd_create returns before thrd_main_func is
99        entered.  So, allocate it in the heap.  */
100     main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
101     if (main_arg == NULL)
102       return ENOMEM;
103     main_arg->a.mainfunc = mainfunc;
104     main_arg->a.arg = arg;
105     switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
106       {
107       case thrd_success:
108         break;
109       case thrd_nomem:
110         free (main_arg);
111         return ENOMEM;
112       default:
113         free (main_arg);
114         return EAGAIN;
115       }
116     *threadp = &main_arg->t;
117     return 0;
118   }
119 }
120 
121 gl_thread_t
gl_thread_self(void)122 gl_thread_self (void)
123 {
124   init_thrd_with_exitvalue_key ();
125   {
126     gl_thread_t thread =
127       (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_key);
128     if (thread == NULL)
129       {
130         /* This happens only in threads that have not been created through
131            glthread_create(), such as the main thread.  */
132         for (;;)
133           {
134             thread =
135               (struct thrd_with_exitvalue *)
136               malloc (sizeof (struct thrd_with_exitvalue));
137             if (thread != NULL)
138               break;
139             /* Memory allocation failed.  There is not much we can do.  Have to
140                busy-loop, waiting for the availability of memory.  */
141             {
142               struct timespec ts;
143               ts.tv_sec = 1;
144               ts.tv_nsec = 0;
145               thrd_sleep (&ts, NULL);
146             }
147           }
148         thread->tid = thrd_current ();
149         thread->exitvalue = NULL; /* just to be deterministic */
150         if (tss_set (thrd_with_exitvalue_key, thread) != thrd_success)
151           abort ();
152       }
153     return thread;
154   }
155 }
156 
157 int
glthread_join(gl_thread_t thread,void ** return_value_ptr)158 glthread_join (gl_thread_t thread, void **return_value_ptr)
159 {
160   /* On Solaris 11.4, thrd_join crashes when the second argument we pass is
161      NULL.  */
162   int dummy;
163 
164   if (thread == gl_thread_self ())
165     return EINVAL;
166   if (thrd_join (thread->tid, &dummy) != thrd_success)
167     return EINVAL;
168   if (return_value_ptr != NULL)
169     *return_value_ptr = thread->exitvalue;
170   free (thread);
171   return 0;
172 }
173 
174 _Noreturn void
gl_thread_exit(void * return_value)175 gl_thread_exit (void *return_value)
176 {
177   gl_thread_t thread = gl_thread_self ();
178   thread->exitvalue = return_value;
179   thrd_exit (0);
180 }
181 
182 #endif
183 
184 /* ========================================================================= */
185 
186 #if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
187 
188 #include <pthread.h>
189 
190 #if defined PTW32_VERSION || defined __MVS__
191 
192 const gl_thread_t gl_null_thread /* = { .p = NULL } */;
193 
194 #endif
195 
196 #endif
197 
198 /* ========================================================================= */
199 
200 #if USE_WINDOWS_THREADS
201 
202 #endif
203 
204 /* ========================================================================= */
205 
206 gl_thread_t
gl_thread_create(void * (* func)(void * arg),void * arg)207 gl_thread_create (void *(*func) (void *arg), void *arg)
208 {
209   gl_thread_t thread;
210   int ret;
211 
212   ret = glthread_create (&thread, func, arg);
213   if (ret != 0)
214     abort ();
215   return thread;
216 }
217