Merge branch 'master' of ssh://bonzini@git.sv.gnu.org/srv/git/gnulib
[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 #include "glthread/thread.h"
25
26 #include <stdlib.h>
27 #include "glthread/lock.h"
28
29 /* ========================================================================= */
30
31 #if USE_WIN32_THREADS
32
33 /* -------------------------- gl_thread_t datatype -------------------------- */
34
35 /* Use a wrapper function, instead of adding WINAPI through a cast.
36    This struct also holds the thread's exit value.  */
37 struct thread_extra
38   {
39     /* Fields for managing the association between thread id and handle.  */
40     DWORD volatile id;
41     HANDLE volatile handle;
42     CRITICAL_SECTION handle_lock;
43     struct thread_extra * volatile next;
44     /* Fields for managing the exit value.  */
45     void * volatile result;
46     /* Fields for managing the thread start.  */
47     void * (*func) (void *);
48     void *arg;
49   };
50
51 /* Linked list of thread_extra of running or zombie (not-yet-joined)
52    threads.
53    TODO: Use a hash table indexed by id instead of a linked list.  */
54 static struct thread_extra *running_threads /* = NULL */;
55
56 /* Lock protecting running_threads.  */
57 gl_lock_define_initialized(static, running_lock)
58
59 static DWORD WINAPI
60 wrapper_func (void *varg)
61 {
62   struct thread_extra *xarg = (struct thread_extra *)varg;
63
64   EnterCriticalSection (&xarg->handle_lock);
65   xarg->id = GetCurrentThreadId ();
66   /* Create a new handle for the thread only if the parent thread did not yet
67      fill in the handle.  */
68   if (xarg->handle == NULL)
69     {
70       HANDLE this_thread;
71       if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
72                             GetCurrentProcess (), &this_thread,
73                             0, FALSE, DUPLICATE_SAME_ACCESS))
74         abort ();
75       xarg->handle = this_thread;
76     }
77   LeaveCriticalSection (&xarg->handle_lock);
78   /* Add xarg to the list of running thread_extra.  */
79   gl_lock_lock (running_lock);
80   if (!(xarg->id == GetCurrentThreadId ()))
81     abort ();
82   xarg->next = running_threads;
83   running_threads = xarg;
84   gl_lock_unlock (running_lock);
85
86   /* Run the thread.  Store the exit value if the thread was not terminated
87      otherwise.  */
88   xarg->result = xarg->func (xarg->arg);
89   return 0;
90 }
91
92 int
93 glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg)
94 {
95   struct thread_extra *x =
96     (struct thread_extra *) malloc (sizeof (struct thread_extra));
97   if (x == NULL)
98     return ENOMEM;
99   x->handle = NULL;
100   InitializeCriticalSection (&x->handle_lock);
101   x->result = NULL; /* just to be deterministic */
102   x->func = func;
103   x->arg = arg;
104   {
105     DWORD thread_id;
106     HANDLE thread_handle;
107
108     thread_handle = CreateThread (NULL, 100000, wrapper_func, x, 0, &thread_id);
109     if (thread_handle == NULL)
110       {
111         DeleteCriticalSection (&x->handle_lock);
112         free (x);
113         return EAGAIN;
114       }
115     EnterCriticalSection (&x->handle_lock);
116     x->id = thread_id;
117     if (x->handle == NULL)
118       x->handle = thread_handle;
119     else
120       /* x->handle was already set by the thread itself.  */
121       CloseHandle (thread_handle);
122     LeaveCriticalSection (&x->handle_lock);
123     *threadp = thread_id;
124     return 0;
125   }
126 }
127
128 int
129 glthread_join_func (gl_thread_t thread, void **retvalp)
130 {
131   HANDLE thread_handle;
132
133   if (thread == gl_thread_self ())
134     return EDEADLK;
135
136   /* Find the thread handle that corresponds to the thread id.
137      The thread argument must come from either the parent thread or from the
138      thread itself.  So at this point, either glthread_create_func or
139      wrapper_func (whichever was executed first) has filled in x->handle.  */
140   thread_handle = NULL;
141   gl_lock_lock (running_lock);
142   {
143     struct thread_extra *x;
144     for (x = running_threads; x != NULL; x = x->next)
145       if (x->id == thread)
146         {
147           thread_handle = x->handle;
148           break;
149         }
150   }
151   gl_lock_unlock (running_lock);
152   if (thread_handle == NULL)
153     return ESRCH;
154
155   if (WaitForSingleObject (thread_handle, INFINITE) == WAIT_FAILED)
156     return EINVAL;
157
158   /* Remove the 'struct thread_extra' from running_threads.  */
159   gl_lock_lock (running_lock);
160   {
161     struct thread_extra * volatile *xp;
162     for (xp = &running_threads; *xp != NULL; xp = &(*xp)->next)
163       if ((*xp)->id == thread)
164         {
165           struct thread_extra *x = *xp;
166           if (retvalp != NULL)
167             *retvalp = x->result;
168           if (x->handle != thread_handle)
169             abort ();
170           *xp = x->next;
171           DeleteCriticalSection (&x->handle_lock);
172           free (x);
173           break;
174         }
175   }
176   gl_lock_unlock (running_lock);
177
178   CloseHandle (thread_handle);
179
180   return 0;
181 }
182
183 int
184 gl_thread_exit_func (void *retval)
185 {
186   DWORD this_thread = GetCurrentThreadId ();
187
188   /* Store the exit value in the appropriate element of running_threads.  */
189   gl_lock_lock (running_lock);
190   {
191     struct thread_extra *x;
192     for (x = running_threads; x != NULL; x = x->next)
193       if (x->id == this_thread)
194         {
195           x->result = retval;
196           break;
197         }
198   }
199   gl_lock_unlock (running_lock);
200
201   ExitThread (0);
202 }
203
204 #endif
205
206 /* ========================================================================= */