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