maint: update copyright
[gnulib.git] / lib / glthread / thread.c
1 /* Creating and controlling threads.
2    Copyright (C) 2005-2014 Free Software Foundation, Inc.
3
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.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://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-solaris.h,
19    gthr-win32.h.  */
20
21 #include <config.h>
22
23 /* Specification.  */
24 # define _GLTHREAD_THREAD_INLINE _GL_EXTERN_INLINE
25 #include "glthread/thread.h"
26
27 #include <stdlib.h>
28 #include "glthread/lock.h"
29
30 /* ========================================================================= */
31
32 #if USE_POSIX_THREADS
33
34 #include <pthread.h>
35
36 #ifdef PTW32_VERSION
37
38 const gl_thread_t gl_null_thread /* = { .p = NULL } */;
39
40 #endif
41
42 #endif
43
44 /* ========================================================================= */
45
46 #if USE_WINDOWS_THREADS
47
48 #include <process.h>
49
50 /* -------------------------- gl_thread_t datatype -------------------------- */
51
52 /* The Thread-Local Storage (TLS) key that allows to access each thread's
53    'struct gl_thread_struct *' pointer.  */
54 static DWORD self_key = (DWORD)-1;
55
56 /* Initializes self_key.  This function must only be called once.  */
57 static void
58 do_init_self_key (void)
59 {
60   self_key = TlsAlloc ();
61   /* If this fails, we're hosed.  */
62   if (self_key == (DWORD)-1)
63     abort ();
64 }
65
66 /* Initializes self_key.  */
67 static void
68 init_self_key (void)
69 {
70   gl_once_define(static, once)
71   gl_once (once, do_init_self_key);
72 }
73
74 /* This structure contains information about a thread.
75    It is stored in TLS under key self_key.  */
76 struct gl_thread_struct
77 {
78   /* Fields for managing the handle.  */
79   HANDLE volatile handle;
80   CRITICAL_SECTION handle_lock;
81   /* Fields for managing the exit value.  */
82   void * volatile result;
83   /* Fields for managing the thread start.  */
84   void * (*func) (void *);
85   void *arg;
86 };
87
88 /* Return a real HANDLE object for the current thread.  */
89 static HANDLE
90 get_current_thread_handle (void)
91 {
92   HANDLE this_handle;
93
94   /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
95      identifier, not a real handle.  */
96   if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
97                         GetCurrentProcess (), &this_handle,
98                         0, FALSE, DUPLICATE_SAME_ACCESS))
99     abort ();
100   return this_handle;
101 }
102
103 gl_thread_t
104 gl_thread_self_func (void)
105 {
106   gl_thread_t thread;
107
108   if (self_key == (DWORD)-1)
109     init_self_key ();
110   thread = TlsGetValue (self_key);
111   if (thread == NULL)
112     {
113       /* This happens only in threads that have not been created through
114          glthread_create(), such as the main thread.  */
115       for (;;)
116         {
117           thread =
118             (struct gl_thread_struct *)
119             malloc (sizeof (struct gl_thread_struct));
120           if (thread != NULL)
121             break;
122           /* Memory allocation failed.  There is not much we can do.  Have to
123              busy-loop, waiting for the availability of memory.  */
124           Sleep (1);
125         }
126
127       thread->handle = get_current_thread_handle ();
128       InitializeCriticalSection (&thread->handle_lock);
129       thread->result = NULL; /* just to be deterministic */
130       TlsSetValue (self_key, thread);
131     }
132   return thread;
133 }
134
135 /* The main function of a freshly creating thread.  It's a wrapper around
136    the FUNC and ARG arguments passed to glthread_create_func.  */
137 static unsigned int WINAPI
138 wrapper_func (void *varg)
139 {
140   struct gl_thread_struct *thread = (struct gl_thread_struct *)varg;
141
142   EnterCriticalSection (&thread->handle_lock);
143   /* Create a new handle for the thread only if the parent thread did not yet
144      fill in the handle.  */
145   if (thread->handle == NULL)
146     thread->handle = get_current_thread_handle ();
147   LeaveCriticalSection (&thread->handle_lock);
148
149   if (self_key == (DWORD)-1)
150     init_self_key ();
151   TlsSetValue (self_key, thread);
152
153   /* Run the thread.  Store the exit value if the thread was not terminated
154      otherwise.  */
155   thread->result = thread->func (thread->arg);
156   return 0;
157 }
158
159 int
160 glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg)
161 {
162   struct gl_thread_struct *thread =
163     (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct));
164   if (thread == NULL)
165     return ENOMEM;
166   thread->handle = NULL;
167   InitializeCriticalSection (&thread->handle_lock);
168   thread->result = NULL; /* just to be deterministic */
169   thread->func = func;
170   thread->arg = arg;
171
172   {
173     unsigned int thread_id;
174     HANDLE thread_handle;
175
176     thread_handle = (HANDLE)
177       _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
178       /* calls CreateThread with the same arguments */
179     if (thread_handle == NULL)
180       {
181         DeleteCriticalSection (&thread->handle_lock);
182         free (thread);
183         return EAGAIN;
184       }
185
186     EnterCriticalSection (&thread->handle_lock);
187     if (thread->handle == NULL)
188       thread->handle = thread_handle;
189     else
190       /* thread->handle was already set by the thread itself.  */
191       CloseHandle (thread_handle);
192     LeaveCriticalSection (&thread->handle_lock);
193
194     *threadp = thread;
195     return 0;
196   }
197 }
198
199 int
200 glthread_join_func (gl_thread_t thread, void **retvalp)
201 {
202   if (thread == NULL)
203     return EINVAL;
204
205   if (thread == gl_thread_self ())
206     return EDEADLK;
207
208   if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
209     return EINVAL;
210
211   if (retvalp != NULL)
212     *retvalp = thread->result;
213
214   DeleteCriticalSection (&thread->handle_lock);
215   CloseHandle (thread->handle);
216   free (thread);
217
218   return 0;
219 }
220
221 int
222 gl_thread_exit_func (void *retval)
223 {
224   gl_thread_t thread = gl_thread_self ();
225   thread->result = retval;
226   _endthreadex (0); /* calls ExitThread (0) */
227   abort ();
228 }
229
230 #endif
231
232 /* ========================================================================= */