]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/cygthread.cc
Cygwin: console: Fix clean up conditions in close()
[newlib-cygwin.git] / winsup / cygwin / cygthread.cc
CommitLineData
b6bd7037
CF
1/* cygthread.cc
2
b6bd7037
CF
3This software is a copyrighted work licensed under the terms of the
4Cygwin license. Please consult the file "CYGWIN_LICENSE" for
5details. */
6
b6bd7037 7#include "winsup.h"
ade47a34 8#include "miscfuncs.h"
d4d59223 9#include <stdlib.h>
1d380f59 10#include "sigproc.h"
d1eb7a46 11#include "cygtls.h"
bd139e52 12#include "ntdll.h"
b6bd7037
CF
13
14#undef CloseHandle
15
36b63207 16static cygthread NO_COPY threads[64];
b6bd7037
CF
17#define NTHREADS (sizeof (threads) / sizeof (threads[0]))
18
b6bd7037 19DWORD NO_COPY cygthread::main_thread_id;
d525130f 20bool NO_COPY cygthread::exiting;
d4d59223 21
267e201d
CF
22void
23cygthread::callfunc (bool issimplestub)
24{
25 void *pass_arg;
26 if (arg == cygself)
27 pass_arg = this;
28 else if (!arglen)
29 pass_arg = arg;
30 else
31 {
32 if (issimplestub)
8d0f58ef 33 ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
267e201d
CF
34 pass_arg = alloca (arglen);
35 memcpy (pass_arg, arg, arglen);
36 SetEvent (ev);
37 }
38 if (issimplestub)
39 {
40 /* Wait for main thread to assign 'h' */
41 while (!h)
084ea510 42 yield ();
267e201d
CF
43 if (ev)
44 CloseHandle (ev);
45 ev = h;
46 }
47 /* Cygwin threads should not call ExitThread directly */
48 func (pass_arg);
49 /* ...so the above should always return */
50}
51
8d0bc156 52/* Initial stub called by cygthread constructor. Performs initial
ec98d19a 53 per-thread initialization and loops waiting for another thread function
8d0bc156 54 to execute. */
b28edc7b 55DWORD
b6bd7037 56cygthread::stub (VOID *arg)
d1eb7a46 57{
b6bd7037 58 cygthread *info = (cygthread *) arg;
8cb359d9 59 _my_tls._ctinfo = info;
c4ec64d7 60 if (info->arg == cygself)
e5d6d535
CF
61 {
62 if (info->ev)
63 {
64 CloseHandle (info->ev);
65 CloseHandle (info->thread_sync);
66 }
2d1d1eb1 67 info->ev = info->thread_sync = info->stack_ptr = NULL;
e5d6d535 68 }
c4ec64d7 69 else
3f5046a5 70 {
73afb2ab 71 info->stack_ptr = &arg;
61522196 72 debug_printf ("thread '%s', id %y, stack_ptr %p", info->name (), info->id, info->stack_ptr);
e5d6d535
CF
73 if (!info->ev)
74 {
8d0f58ef 75 info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
e5d6d535
CF
76 info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
77 }
3f5046a5 78 }
2d1d1eb1 79
b6bd7037
CF
80 while (1)
81 {
63fcc6d4
CF
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
a7a5d0ba
CF
88 else
89 {
b79b0c2b 90 SetThreadName (info->id, info->__name);
267e201d 91 info->callfunc (false);
b6bd7037 92
ec98d19a 93 HANDLE notify = info->notify_detached;
74d8e12e 94 /* If func is NULL, the above function has set that to indicate
4ee52924
CF
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
ec98d19a 97 what it is doing. */
4ee52924
CF
98 if (!info->func)
99 info->release (false);
100 else
101 {
1524ae42 102#ifdef DEBUGGING
4ee52924
CF
103 info->func = NULL; // catch erroneous activation
104 info->__oldname = info->__name;
1524ae42 105#endif
4ee52924
CF
106 info->__name = NULL;
107 SetEvent (info->ev);
108 }
ec98d19a
CF
109 if (notify)
110 SetEvent (notify);
a7a5d0ba 111 }
3f5046a5
CF
112 switch (WaitForSingleObject (info->thread_sync, INFINITE))
113 {
114 case WAIT_OBJECT_0:
115 continue;
116 default:
117 api_fatal ("WFSO failed, %E");
118 break;
119 }
b6bd7037
CF
120 }
121}
122
3f5046a5
CF
123/* Overflow stub called by cygthread constructor. Calls specified function
124 and then exits the thread. */
b28edc7b 125DWORD
3f5046a5 126cygthread::simplestub (VOID *arg)
d1eb7a46 127{
3f5046a5 128 cygthread *info = (cygthread *) arg;
8cb359d9 129 _my_tls._ctinfo = info;
73afb2ab 130 info->stack_ptr = &arg;
2691168e 131 HANDLE notify = info->notify_detached;
b79b0c2b 132 SetThreadName (info->id, info->__name);
267e201d 133 info->callfunc (true);
2691168e
CF
134 if (notify)
135 SetEvent (notify);
c350feda 136 return 0;
3f5046a5
CF
137}
138
8d0bc156 139/* Start things going. Called from dll_crt0_1. */
b6bd7037
CF
140void
141cygthread::init ()
142{
b6bd7037
CF
143 main_thread_id = GetCurrentThreadId ();
144}
145
d525130f 146cygthread *
d4d59223
CF
147cygthread::freerange ()
148{
149 cygthread *self = (cygthread *) calloc (1, sizeof (*self));
150 self->is_freerange = true;
ef4d65ba 151 self->inuse = 1;
d4d59223
CF
152 return self;
153}
154
b6bd7037
CF
155void * cygthread::operator
156new (size_t)
157{
8d0bc156 158 cygthread *info;
b6bd7037 159
d525130f
CF
160 /* Search the threads array for an empty slot to use */
161 for (info = threads; info < threads + NTHREADS; info++)
e5d6d535 162 if (!InterlockedExchange (&info->inuse, 1))
d525130f 163 {
9dbe3289 164 /* available */
1524ae42 165#ifdef DEBUGGING
d525130f 166 if (info->__name)
61522196 167 api_fatal ("name not NULL? %s, id %y, i %ld", info->__name, info->id, info - threads);
1524ae42 168#endif
d525130f
CF
169 goto out;
170 }
aea1f301 171
5bf785a0 172#ifdef DEBUGGING
faf42105
CF
173 if (!getenv ("CYGWIN_FREERANGE_NOCHECK"))
174 api_fatal ("overflowed cygwin thread pool");
ef4d65ba 175 else
faf42105 176 thread_printf ("overflowed cygwin thread pool");
5bf785a0 177#endif
d525130f
CF
178
179 info = freerange (); /* exhausted thread pool */
180
181out:
d525130f 182 return info;
b6bd7037
CF
183}
184
53ad6f13
CF
185/* This function is called via QueueUserAPC. Apparently creating threads
186 asynchronously is a huge performance win on Win64. */
187void CALLBACK
188cygthread::async_create (ULONG_PTR arg)
189{
190 cygthread *that = (cygthread *) arg;
191 that->create ();
192 ::SetThreadPriority (that->h, THREAD_PRIORITY_HIGHEST);
193 that->zap_h ();
194}
195
4db1bd40
CF
196void
197cygthread::create ()
b6bd7037 198{
61522196 199 thread_printf ("name %s, id %y, this %p", __name, id, this);
267e201d 200 HANDLE htobe;
e5d6d535 201 if (h)
5ffec1d1 202 {
267e201d
CF
203 if (ev)
204 ResetEvent (ev);
e5d6d535 205 while (!thread_sync)
084ea510 206 yield ();
e5d6d535 207 SetEvent (thread_sync);
61522196 208 thread_printf ("activated name '%s', thread_sync %p for id %y", __name, thread_sync, id);
267e201d 209 htobe = h;
5ffec1d1 210 }
5ec14fe4 211 else
e5d6d535 212 {
73afb2ab 213 stack_ptr = NULL;
267e201d
CF
214 htobe = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
215 this, 0, &id);
216 if (!htobe)
61522196
CV
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);
7a2ba9db 219#ifdef DEBUGGING
63fcc6d4 220 terminated = false;
7a2ba9db 221#endif
e5d6d535 222 }
267e201d 223
4db1bd40 224 if (arglen)
267e201d
CF
225 {
226 while (!ev)
084ea510 227 yield ();
267e201d
CF
228 WaitForSingleObject (ev, INFINITE);
229 ResetEvent (ev);
230 }
231 h = htobe;
b6bd7037
CF
232}
233
234/* Return the symbolic name of the current thread for debugging.
235 */
236const char *
237cygthread::name (DWORD tid)
238{
239 const char *res = NULL;
240 if (!tid)
241 tid = GetCurrentThreadId ();
242
243 if (tid == main_thread_id)
244 return "main";
245
246 for (DWORD i = 0; i < NTHREADS; i++)
247 if (threads[i].id == tid)
248 {
249 res = threads[i].__name ?: "exiting thread";
250 break;
251 }
252
e553226c
CF
253 if (res)
254 /* ok */;
255 else if (!_main_tls)
256 res = "main";
257 else
b6bd7037 258 {
61522196 259 __small_sprintf (_my_tls.locals.unknown_thread_name, "unknown (%y)", tid);
24515d65 260 res = _my_tls.locals.unknown_thread_name;
b6bd7037 261 }
b6bd7037
CF
262 return res;
263}
264
265cygthread::operator
266HANDLE ()
267{
268 while (!ev)
084ea510 269 yield ();
b6bd7037
CF
270 return ev;
271}
272
9c0d960d 273void
63fcc6d4 274cygthread::release (bool nuke_h)
9c0d960d 275{
63fcc6d4
CF
276 if (nuke_h)
277 h = NULL;
7a2ba9db 278#ifdef DEBUGGING
63fcc6d4 279 __oldname = __name;
538776b7 280 debug_printf ("released thread '%s'", __oldname);
7a2ba9db 281#endif
9c0d960d 282 __name = NULL;
4ee52924 283 func = NULL;
e41ff609 284 /* Must be last */
4ee52924
CF
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
9c0d960d
CF
291}
292
1d380f59 293/* Forcibly terminate a thread. */
85a798d6 294bool
1d380f59
CF
295cygthread::terminate_thread ()
296{
85a798d6 297 bool terminated = true;
61522196 298 debug_printf ("thread '%s', id %y, inuse %d, stack_ptr %p", __name, id, inuse, stack_ptr);
57ba174f 299 while (inuse && !stack_ptr)
084ea510 300 yield ();
57ba174f 301
63fcc6d4 302 if (!inuse)
cc9440b6 303 goto force_notterminated;
264f41f0 304
0c55f6ed
CF
305 TerminateThread (h, 0);
306 WaitForSingleObject (h, INFINITE);
e5d6d535
CF
307 CloseHandle (h);
308
cc9440b6
CF
309 if (!inuse || exiting)
310 goto force_notterminated;
311
bd139e52 312 if (ev && !(terminated = !IsEventSignalled (ev)))
8d0f58ef 313 ResetEvent (ev);
9bc36097 314
3786526e
CF
315 if (is_freerange)
316 free (this);
317 else
8d0f58ef
CF
318 {
319#ifdef DEBUGGING
320 terminated = true;
321#endif
322 release (true);
323 }
264f41f0 324
cc9440b6
CF
325 goto out;
326
327force_notterminated:
328 terminated = false;
329out:
85a798d6 330 return terminated;
1d380f59
CF
331}
332
8d0bc156 333/* Detach the cygthread from the current thread. Note that the
3f5046a5 334 theory is that cygthreads are only associated with one thread.
9dbe3289
CF
335 So, there should be never be multiple threads doing waits
336 on the same cygthread. */
8bce0d72
CF
337bool
338cygthread::detach (HANDLE sigwait)
b6bd7037 339{
8bce0d72 340 bool signalled = false;
cc9440b6 341 bool thread_was_reset = false;
e5d6d535 342 if (!inuse)
61522196 343 system_printf ("called detach but inuse %d, thread %y?", inuse, id);
1524ae42 344 else
b6bd7037 345 {
1d380f59
CF
346 DWORD res;
347
8bce0d72 348 if (!sigwait)
ec98d19a
CF
349 /* If the caller specified a special handle for notification, wait for that.
350 This assumes that the thread in question is auto releasing. */
dc000a83 351 res = WaitForSingleObject (*this, INFINITE);
1d380f59
CF
352 else
353 {
cc9440b6
CF
354 /* Lower our priority and give priority to the read thread */
355 HANDLE hth = GetCurrentThread ();
356 LONG prio = GetThreadPriority (hth);
0c55f6ed 357 ::SetThreadPriority (hth, THREAD_PRIORITY_BELOW_NORMAL);
cc9440b6 358
2addde8c
CF
359 HANDLE w4[2];
360 unsigned n = 2;
cc9440b6 361 DWORD howlong = INFINITE;
2addde8c 362 w4[0] = sigwait;
c43e9340 363 wait_signal_arrived here (w4[1]);
cc9440b6
CF
364 /* For a description of the below loop see the end of this file */
365 for (int i = 0; i < 2; i++)
2addde8c 366 switch (res = WaitForMultipleObjects (n, w4, FALSE, howlong))
cc9440b6
CF
367 {
368 case WAIT_OBJECT_0:
369 if (n == 1)
370 howlong = 50;
371 break;
2addde8c 372 case WAIT_OBJECT_0 + 1:
cc9440b6
CF
373 n = 1;
374 if (i--)
375 howlong = 50;
376 break;
377 case WAIT_TIMEOUT:
378 break;
379 default:
380 if (!exiting)
2addde8c
CF
381 {
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))
385 {
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;
392 }
393 api_fatal ("exiting on fatal error");
394 }
cc9440b6
CF
395 break;
396 }
397 /* WAIT_OBJECT_0 means that the thread successfully read something,
398 so wait for the cygthread to "terminate". */
9508ebc5 399 if (res == WAIT_OBJECT_0)
0c55f6ed 400 WaitForSingleObject (*this, INFINITE);
85a798d6 401 else
1d380f59 402 {
cc9440b6
CF
403 /* Thread didn't terminate on its own, so maybe we have to
404 do it. */
85a798d6 405 signalled = terminate_thread ();
cc9440b6
CF
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;
1d380f59 414 }
0c55f6ed 415 ::SetThreadPriority (hth, prio);
1d380f59
CF
416 }
417
61522196 418 thread_printf ("%s returns %d, id %y", sigwait ? "WFMO" : "WFSO",
1d380f59 419 res, id);
5ec14fe4 420
cc9440b6 421 if (thread_was_reset)
1d380f59
CF
422 /* already handled */;
423 else if (is_freerange)
d4d59223
CF
424 {
425 CloseHandle (h);
426 free (this);
427 }
428 else
429 {
5ec14fe4 430 ResetEvent (*this);
e5d6d535 431 /* Mark the thread as available by setting inuse to zero */
0c55f6ed 432 InterlockedExchange (&inuse, 0);
d4d59223 433 }
b6bd7037 434 }
8bce0d72 435 return signalled;
b6bd7037 436}
aea1f301
CF
437
438void
439cygthread::terminate ()
440{
d525130f 441 exiting = 1;
aea1f301 442}
cc9440b6
CF
443
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.
449
450 case 0 - no signal
451
452 i == 0 (howlong == INFINITE)
453 W0 activated
454 howlong not set because n != 1
455 just loop
456
457 i == 1 (howlong == INFINITE)
458 W0 activated
459 howlong not set because n != 1
460 just loop (to exit loop) - no signal
461
462 i == 2 (howlong == INFINITE)
463 exit loop
464
465 case 1 - signal before thread initialized
466
467 i == 0 (howlong == INFINITE)
468 WO + 1 activated
469 n set to 1
470 howlong untouched because i-- == 0
471 loop
472
473 i == 0 (howlong == INFINITE)
474 W0 must be activated
475 howlong set to 50 because n == 1
476
477 i == 1 (howlong == 50)
478 W0 activated
479 loop (to exit loop) - no signal
480
481 WAIT_TIMEOUT activated
482 signal potentially detected
483 loop (to exit loop)
484
485 i == 2 (howlong == 50)
486 exit loop
487
488 case 2 - signal after thread initialized
489
490 i == 0 (howlong == INFINITE)
491 W0 activated
492 howlong not set because n != 1
493 loop
494
495 i == 1 (howlong == INFINITE)
496 W0 + 1 activated
497 n set to 1
498 howlong set to 50 because i-- != 0
499 loop
500
501 i == 1 (howlong == 50)
502 W0 activated
503 loop (to exit loop) - no signal
504
505 WAIT_TIMEOUT activated
506 loop (to exit loop) - signal
507
508 i == 2 (howlong == 50)
509 exit loop
510*/
This page took 0.50148 seconds and 6 git commands to generate.