Bump to m4 1.4.19
[platform/upstream/m4.git] / tests / windows-thread.c
1 /* Creating and controlling threads (native Windows implementation).
2    Copyright (C) 2005-2021 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, see <https://www.gnu.org/licenses/>.  */
16
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.
18    Based on GCC's gthr-win32.h.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include "windows-thread.h"
24
25 #include <errno.h>
26 #include <process.h>
27 #include <stdlib.h>
28
29 #include "windows-once.h"
30 #include "windows-tls.h"
31
32 /* The Thread-Local Storage (TLS) key that allows to access each thread's
33    'struct glwthread_thread_struct *' pointer.  */
34 static DWORD self_key = (DWORD)-1;
35
36 /* Initializes self_key.  This function must only be called once.  */
37 static void
38 do_init_self_key (void)
39 {
40   self_key = TlsAlloc ();
41   /* If this fails, we're hosed.  */
42   if (self_key == (DWORD)-1)
43     abort ();
44 }
45
46 /* Initializes self_key.  */
47 static void
48 init_self_key (void)
49 {
50   static glwthread_once_t once = GLWTHREAD_ONCE_INIT;
51   glwthread_once (&once, do_init_self_key);
52 }
53
54 /* This structure contains information about a thread.
55    It is stored in TLS under key self_key.  */
56 struct glwthread_thread_struct
57 {
58   /* Fields for managing the handle.  */
59   HANDLE volatile handle;
60   CRITICAL_SECTION handle_lock;
61   /* Fields for managing the exit value.  */
62   BOOL volatile detached;
63   void * volatile result;
64   /* Fields for managing the thread start.  */
65   void * (*func) (void *);
66   void *arg;
67 };
68
69 /* Return a real HANDLE object for the current thread.  */
70 static HANDLE
71 get_current_thread_handle (void)
72 {
73   HANDLE this_handle;
74
75   /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
76      identifier, not a real handle.  */
77   if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
78                         GetCurrentProcess (), &this_handle,
79                         0, FALSE, DUPLICATE_SAME_ACCESS))
80     abort ();
81   return this_handle;
82 }
83
84 glwthread_thread_t
85 glwthread_thread_self (void)
86 {
87   glwthread_thread_t thread;
88
89   if (self_key == (DWORD)-1)
90     init_self_key ();
91   thread = TlsGetValue (self_key);
92   if (thread == NULL)
93     {
94       /* This happens only in threads that have not been created through
95          glthread_create(), such as the main thread.  */
96       for (;;)
97         {
98           thread =
99             (struct glwthread_thread_struct *)
100             malloc (sizeof (struct glwthread_thread_struct));
101           if (thread != NULL)
102             break;
103           /* Memory allocation failed.  There is not much we can do.  Have to
104              busy-loop, waiting for the availability of memory.  */
105           Sleep (1);
106         }
107
108       thread->handle = get_current_thread_handle ();
109       InitializeCriticalSection (&thread->handle_lock);
110       thread->detached = FALSE; /* This can lead to a memory leak.  */
111       thread->result = NULL; /* just to be deterministic */
112       TlsSetValue (self_key, thread);
113     }
114   return thread;
115 }
116
117 /* The main function of a freshly creating thread.  It's a wrapper around
118    the FUNC and ARG arguments passed to glthread_create_func.  */
119 static unsigned int WINAPI
120 wrapper_func (void *varg)
121 {
122   struct glwthread_thread_struct *thread =
123     (struct glwthread_thread_struct *) varg;
124
125   EnterCriticalSection (&thread->handle_lock);
126   /* Create a new handle for the thread only if the parent thread did not yet
127      fill in the handle.  */
128   if (thread->handle == NULL)
129     thread->handle = get_current_thread_handle ();
130   LeaveCriticalSection (&thread->handle_lock);
131
132   if (self_key == (DWORD)-1)
133     init_self_key ();
134   TlsSetValue (self_key, thread);
135
136   /* Run the thread.  Store the exit value if the thread was not terminated
137      otherwise.  */
138   thread->result = thread->func (thread->arg);
139
140   /* Process the TLS destructors.  */
141   glwthread_tls_process_destructors ();
142
143   if (thread->detached)
144     {
145       /* Clean up the thread, like thrd_join would do.  */
146       DeleteCriticalSection (&thread->handle_lock);
147       CloseHandle (thread->handle);
148       free (thread);
149     }
150
151   return 0;
152 }
153
154 int
155 glwthread_thread_create (glwthread_thread_t *threadp, unsigned int attr,
156                          void * (*func) (void *), void *arg)
157 {
158   struct glwthread_thread_struct *thread =
159     (struct glwthread_thread_struct *)
160     malloc (sizeof (struct glwthread_thread_struct));
161   if (thread == NULL)
162     return ENOMEM;
163   thread->handle = NULL;
164   InitializeCriticalSection (&thread->handle_lock);
165   thread->detached = (attr & GLWTHREAD_ATTR_DETACHED ? TRUE : FALSE);
166   thread->result = NULL; /* just to be deterministic */
167   thread->func = func;
168   thread->arg = arg;
169
170   {
171     unsigned int thread_id;
172     HANDLE thread_handle;
173
174     thread_handle = (HANDLE)
175       _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
176       /* calls CreateThread with the same arguments */
177     if (thread_handle == NULL)
178       {
179         DeleteCriticalSection (&thread->handle_lock);
180         free (thread);
181         return EAGAIN;
182       }
183
184     EnterCriticalSection (&thread->handle_lock);
185     if (thread->handle == NULL)
186       thread->handle = thread_handle;
187     else
188       /* thread->handle was already set by the thread itself.  */
189       CloseHandle (thread_handle);
190     LeaveCriticalSection (&thread->handle_lock);
191
192     *threadp = thread;
193     return 0;
194   }
195 }
196
197 int
198 glwthread_thread_join (glwthread_thread_t thread, void **retvalp)
199 {
200   if (thread == NULL)
201     return EINVAL;
202
203   if (thread == glwthread_thread_self ())
204     return EDEADLK;
205
206   if (thread->detached)
207     return EINVAL;
208
209   if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
210     return EINVAL;
211
212   if (retvalp != NULL)
213     *retvalp = thread->result;
214
215   DeleteCriticalSection (&thread->handle_lock);
216   CloseHandle (thread->handle);
217   free (thread);
218
219   return 0;
220 }
221
222 int
223 glwthread_thread_detach (glwthread_thread_t thread)
224 {
225   if (thread == NULL)
226     return EINVAL;
227
228   if (thread->detached)
229     return EINVAL;
230
231   thread->detached = TRUE;
232   return 0;
233 }
234
235 void
236 glwthread_thread_exit (void *retval)
237 {
238   glwthread_thread_t thread = glwthread_thread_self ();
239   thread->result = retval;
240   glwthread_tls_process_destructors ();
241   _endthreadex (0); /* calls ExitThread (0) */
242   abort ();
243 }