Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / cygthread.cc
blob54918e767784fe033f802ea5e15773d3c7adb58f
1 /* cygthread.cc
3 This software is a copyrighted work licensed under the terms of the
4 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
5 details. */
7 #include "winsup.h"
8 #include "miscfuncs.h"
9 #include <stdlib.h>
10 #include "sigproc.h"
11 #include "cygtls.h"
12 #include "ntdll.h"
14 #undef CloseHandle
16 static cygthread NO_COPY threads[64];
17 #define NTHREADS (sizeof (threads) / sizeof (threads[0]))
19 DWORD NO_COPY cygthread::main_thread_id;
20 bool NO_COPY cygthread::exiting;
22 void
23 cygthread::callfunc (bool issimplestub)
25 void *pass_arg;
26 if (arg == cygself)
27 pass_arg = this;
28 else if (!arglen)
29 pass_arg = arg;
30 else
32 if (issimplestub)
33 ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
34 pass_arg = alloca (arglen);
35 memcpy (pass_arg, arg, arglen);
36 SetEvent (ev);
38 if (issimplestub)
40 /* Wait for main thread to assign 'h' */
41 while (!h)
42 yield ();
43 if (ev)
44 CloseHandle (ev);
45 ev = h;
47 /* Cygwin threads should not call ExitThread directly */
48 func (pass_arg);
49 /* ...so the above should always return */
52 /* Initial stub called by cygthread constructor. Performs initial
53 per-thread initialization and loops waiting for another thread function
54 to execute. */
55 DWORD
56 cygthread::stub (VOID *arg)
58 cygthread *info = (cygthread *) arg;
59 _my_tls._ctinfo = info;
60 if (info->arg == cygself)
62 if (info->ev)
64 CloseHandle (info->ev);
65 CloseHandle (info->thread_sync);
67 info->ev = info->thread_sync = info->stack_ptr = NULL;
69 else
71 info->stack_ptr = &arg;
72 debug_printf ("thread '%s', id %y, stack_ptr %p", info->name (), info->id, info->stack_ptr);
73 if (!info->ev)
75 info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
76 info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
80 while (1)
82 if (!info->__name)
83 #ifdef DEBUGGING
84 system_printf ("erroneous thread activation, name is NULL prev thread name = '%s'", info->__oldname);
85 #else
86 system_printf ("erroneous thread activation, name is NULL");
87 #endif
88 else
90 SetThreadName (info->id, info->__name);
91 info->callfunc (false);
93 HANDLE notify = info->notify_detached;
94 /* If func is NULL, the above function has set that to indicate
95 that it doesn't want to alert anyone with a SetEvent and should
96 just be marked as no longer inuse. Hopefully the function knows
97 what it is doing. */
98 if (!info->func)
99 info->release (false);
100 else
102 #ifdef DEBUGGING
103 info->func = NULL; // catch erroneous activation
104 info->__oldname = info->__name;
105 #endif
106 info->__name = NULL;
107 SetEvent (info->ev);
109 if (notify)
110 SetEvent (notify);
112 switch (WaitForSingleObject (info->thread_sync, INFINITE))
114 case WAIT_OBJECT_0:
115 continue;
116 default:
117 api_fatal ("WFSO failed, %E");
118 break;
123 /* Overflow stub called by cygthread constructor. Calls specified function
124 and then exits the thread. */
125 DWORD
126 cygthread::simplestub (VOID *arg)
128 cygthread *info = (cygthread *) arg;
129 _my_tls._ctinfo = info;
130 info->stack_ptr = &arg;
131 HANDLE notify = info->notify_detached;
132 SetThreadName (info->id, info->__name);
133 info->callfunc (true);
134 if (notify)
135 SetEvent (notify);
136 return 0;
139 /* Start things going. Called from dll_crt0_1. */
140 void
141 cygthread::init ()
143 main_thread_id = GetCurrentThreadId ();
146 cygthread *
147 cygthread::freerange ()
149 cygthread *self = (cygthread *) calloc (1, sizeof (*self));
150 self->is_freerange = true;
151 self->inuse = 1;
152 return self;
155 void * cygthread::operator
156 new (size_t)
158 cygthread *info;
160 /* Search the threads array for an empty slot to use */
161 for (info = threads; info < threads + NTHREADS; info++)
162 if (!InterlockedExchange (&info->inuse, 1))
164 /* available */
165 #ifdef DEBUGGING
166 if (info->__name)
167 api_fatal ("name not NULL? %s, id %y, i %ld", info->__name, info->id, info - threads);
168 #endif
169 goto out;
172 #ifdef DEBUGGING
173 if (!getenv ("CYGWIN_FREERANGE_NOCHECK"))
174 api_fatal ("overflowed cygwin thread pool");
175 else
176 thread_printf ("overflowed cygwin thread pool");
177 #endif
179 info = freerange (); /* exhausted thread pool */
181 out:
182 return info;
185 /* This function is called via QueueUserAPC. Apparently creating threads
186 asynchronously is a huge performance win on Win64. */
187 void CALLBACK
188 cygthread::async_create (ULONG_PTR arg)
190 cygthread *that = (cygthread *) arg;
191 that->create ();
192 ::SetThreadPriority (that->h, THREAD_PRIORITY_HIGHEST);
193 that->zap_h ();
196 void
197 cygthread::create ()
199 thread_printf ("name %s, id %y, this %p", __name, id, this);
200 HANDLE htobe;
201 if (h)
203 if (ev)
204 ResetEvent (ev);
205 while (!thread_sync)
206 yield ();
207 SetEvent (thread_sync);
208 thread_printf ("activated name '%s', thread_sync %p for id %y", __name, thread_sync, id);
209 htobe = h;
211 else
213 stack_ptr = NULL;
214 htobe = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
215 this, 0, &id);
216 if (!htobe)
217 api_fatal ("CreateThread failed for %s - %p<%y>, %E", __name, h, id);
218 thread_printf ("created name '%s', thread %p, id %y", __name, h, id);
219 #ifdef DEBUGGING
220 terminated = false;
221 #endif
224 if (arglen)
226 while (!ev)
227 yield ();
228 WaitForSingleObject (ev, INFINITE);
229 ResetEvent (ev);
231 h = htobe;
234 /* Return the symbolic name of the current thread for debugging.
236 const char *
237 cygthread::name (DWORD tid)
239 const char *res = NULL;
240 if (!tid)
241 tid = GetCurrentThreadId ();
243 if (tid == main_thread_id)
244 return "main";
246 for (DWORD i = 0; i < NTHREADS; i++)
247 if (threads[i].id == tid)
249 res = threads[i].__name ?: "exiting thread";
250 break;
253 if (res)
254 /* ok */;
255 else if (!_main_tls)
256 res = "main";
257 else
259 __small_sprintf (_my_tls.locals.unknown_thread_name, "unknown (%y)", tid);
260 res = _my_tls.locals.unknown_thread_name;
262 return res;
265 cygthread::operator
266 HANDLE ()
268 while (!ev)
269 yield ();
270 return ev;
273 void
274 cygthread::release (bool nuke_h)
276 if (nuke_h)
277 h = NULL;
278 #ifdef DEBUGGING
279 __oldname = __name;
280 debug_printf ("released thread '%s'", __oldname);
281 #endif
282 __name = NULL;
283 func = NULL;
284 /* Must be last */
285 if (!InterlockedExchange (&inuse, 0))
286 #ifdef DEBUGGING
287 api_fatal ("released a thread that was not inuse");
288 #else
289 system_printf ("released a thread that was not inuse");
290 #endif
293 /* Forcibly terminate a thread. */
294 bool
295 cygthread::terminate_thread ()
297 bool terminated = true;
298 debug_printf ("thread '%s', id %y, inuse %d, stack_ptr %p", __name, id, inuse, stack_ptr);
299 while (inuse && !stack_ptr)
300 yield ();
302 if (!inuse)
303 goto force_notterminated;
305 TerminateThread (h, 0);
306 WaitForSingleObject (h, INFINITE);
307 CloseHandle (h);
309 if (!inuse || exiting)
310 goto force_notterminated;
312 if (ev && !(terminated = !IsEventSignalled (ev)))
313 ResetEvent (ev);
315 if (is_freerange)
316 free (this);
317 else
319 #ifdef DEBUGGING
320 terminated = true;
321 #endif
322 release (true);
325 goto out;
327 force_notterminated:
328 terminated = false;
329 out:
330 return terminated;
333 /* Detach the cygthread from the current thread. Note that the
334 theory is that cygthreads are only associated with one thread.
335 So, there should be never be multiple threads doing waits
336 on the same cygthread. */
337 bool
338 cygthread::detach (HANDLE sigwait)
340 bool signalled = false;
341 bool thread_was_reset = false;
342 if (!inuse)
343 system_printf ("called detach but inuse %d, thread %y?", inuse, id);
344 else
346 DWORD res;
348 if (!sigwait)
349 /* If the caller specified a special handle for notification, wait for that.
350 This assumes that the thread in question is auto releasing. */
351 res = WaitForSingleObject (*this, INFINITE);
352 else
354 /* Lower our priority and give priority to the read thread */
355 HANDLE hth = GetCurrentThread ();
356 LONG prio = GetThreadPriority (hth);
357 ::SetThreadPriority (hth, THREAD_PRIORITY_BELOW_NORMAL);
359 HANDLE w4[2];
360 unsigned n = 2;
361 DWORD howlong = INFINITE;
362 w4[0] = sigwait;
363 wait_signal_arrived here (w4[1]);
364 /* For a description of the below loop see the end of this file */
365 for (int i = 0; i < 2; i++)
366 switch (res = WaitForMultipleObjects (n, w4, FALSE, howlong))
368 case WAIT_OBJECT_0:
369 if (n == 1)
370 howlong = 50;
371 break;
372 case WAIT_OBJECT_0 + 1:
373 n = 1;
374 if (i--)
375 howlong = 50;
376 break;
377 case WAIT_TIMEOUT:
378 break;
379 default:
380 if (!exiting)
382 system_printf ("WFMO failed waiting for cygthread '%s', %E", __name);
383 for (unsigned j = 0; j < n; j++)
384 switch (WaitForSingleObject (w4[j], 0))
386 case WAIT_OBJECT_0:
387 case WAIT_TIMEOUT:
388 break;
389 default:
390 system_printf ("%s handle %p is bad", (j ? "signal_arrived" : "semaphore"), w4[j]);
391 break;
393 api_fatal ("exiting on fatal error");
395 break;
397 /* WAIT_OBJECT_0 means that the thread successfully read something,
398 so wait for the cygthread to "terminate". */
399 if (res == WAIT_OBJECT_0)
400 WaitForSingleObject (*this, INFINITE);
401 else
403 /* Thread didn't terminate on its own, so maybe we have to
404 do it. */
405 signalled = terminate_thread ();
406 /* Possibly the thread completed *just* before it was
407 terminated. Detect this. If this happened then the
408 read was not terminated on a signal. */
409 if (WaitForSingleObject (sigwait, 0) == WAIT_OBJECT_0)
410 signalled = false;
411 if (signalled)
412 set_sig_errno (EINTR);
413 thread_was_reset = true;
415 ::SetThreadPriority (hth, prio);
418 thread_printf ("%s returns %d, id %y", sigwait ? "WFMO" : "WFSO",
419 res, id);
421 if (thread_was_reset)
422 /* already handled */;
423 else if (is_freerange)
425 CloseHandle (h);
426 free (this);
428 else
430 ResetEvent (*this);
431 /* Mark the thread as available by setting inuse to zero */
432 InterlockedExchange (&inuse, 0);
435 return signalled;
438 void
439 cygthread::terminate ()
441 exiting = 1;
444 /* The below is an explanation of synchronization loop in cygthread::detach.
445 The intent is that the loop will always try hard to wait for both
446 synchronization events from the reader thread but will exit with
447 res == WAIT_TIMEOUT if a signal occurred and the reader thread is
448 still blocked.
450 case 0 - no signal
452 i == 0 (howlong == INFINITE)
453 W0 activated
454 howlong not set because n != 1
455 just loop
457 i == 1 (howlong == INFINITE)
458 W0 activated
459 howlong not set because n != 1
460 just loop (to exit loop) - no signal
462 i == 2 (howlong == INFINITE)
463 exit loop
465 case 1 - signal before thread initialized
467 i == 0 (howlong == INFINITE)
468 WO + 1 activated
469 n set to 1
470 howlong untouched because i-- == 0
471 loop
473 i == 0 (howlong == INFINITE)
474 W0 must be activated
475 howlong set to 50 because n == 1
477 i == 1 (howlong == 50)
478 W0 activated
479 loop (to exit loop) - no signal
481 WAIT_TIMEOUT activated
482 signal potentially detected
483 loop (to exit loop)
485 i == 2 (howlong == 50)
486 exit loop
488 case 2 - signal after thread initialized
490 i == 0 (howlong == INFINITE)
491 W0 activated
492 howlong not set because n != 1
493 loop
495 i == 1 (howlong == INFINITE)
496 W0 + 1 activated
497 n set to 1
498 howlong set to 50 because i-- != 0
499 loop
501 i == 1 (howlong == 50)
502 W0 activated
503 loop (to exit loop) - no signal
505 WAIT_TIMEOUT activated
506 loop (to exit loop) - signal
508 i == 2 (howlong == 50)
509 exit loop