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