]>
Commit | Line | Data |
---|---|---|
b6bd7037 CF |
1 | /* cygthread.cc |
2 | ||
b6bd7037 CF |
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. */ | |
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 | 16 | static cygthread NO_COPY threads[64]; |
b6bd7037 CF |
17 | #define NTHREADS (sizeof (threads) / sizeof (threads[0])) |
18 | ||
b6bd7037 | 19 | DWORD NO_COPY cygthread::main_thread_id; |
d525130f | 20 | bool NO_COPY cygthread::exiting; |
d4d59223 | 21 | |
267e201d CF |
22 | void |
23 | cygthread::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 | 55 | DWORD |
b6bd7037 | 56 | cygthread::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 | 125 | DWORD |
3f5046a5 | 126 | cygthread::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 |
140 | void |
141 | cygthread::init () | |
142 | { | |
b6bd7037 CF |
143 | main_thread_id = GetCurrentThreadId (); |
144 | } | |
145 | ||
d525130f | 146 | cygthread * |
d4d59223 CF |
147 | cygthread::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 |
155 | void * cygthread::operator |
156 | new (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 | ||
181 | out: | |
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. */ | |
187 | void CALLBACK | |
188 | cygthread::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 |
196 | void |
197 | cygthread::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 | */ | |
236 | const char * | |
237 | cygthread::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 | ||
265 | cygthread::operator | |
266 | HANDLE () | |
267 | { | |
268 | while (!ev) | |
084ea510 | 269 | yield (); |
b6bd7037 CF |
270 | return ev; |
271 | } | |
272 | ||
9c0d960d | 273 | void |
63fcc6d4 | 274 | cygthread::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 | 294 | bool |
1d380f59 CF |
295 | cygthread::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 | ||
327 | force_notterminated: | |
328 | terminated = false; | |
329 | out: | |
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 |
337 | bool |
338 | cygthread::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 | |
438 | void | |
439 | cygthread::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 | */ |