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