]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/cygthread.cc
Revert errneous checkin.
[newlib-cygwin.git] / winsup / cygwin / cygthread.cc
1 /* cygthread.cc
2
3 Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008,
4 2009, 2010, 2011 Red Hat, Inc.
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
10 #include "winsup.h"
11 #include "miscfuncs.h"
12 #include <stdlib.h>
13 #include "sigproc.h"
14 #include "cygtls.h"
15 #include "ntdll.h"
16
17 #undef CloseHandle
18
19 static cygthread NO_COPY threads[64];
20 #define NTHREADS (sizeof (threads) / sizeof (threads[0]))
21
22 DWORD NO_COPY cygthread::main_thread_id;
23 bool NO_COPY cygthread::exiting;
24
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)
36 ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
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)
45 yield ();
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
55 /* Initial stub called by cygthread constructor. Performs initial
56 per-thread initialization and loops waiting for another thread function
57 to execute. */
58 DWORD WINAPI
59 cygthread::stub (VOID *arg)
60 {
61 cygthread *info = (cygthread *) arg;
62 _my_tls._ctinfo = info;
63 if (info->arg == cygself)
64 {
65 if (info->ev)
66 {
67 CloseHandle (info->ev);
68 CloseHandle (info->thread_sync);
69 }
70 info->ev = info->thread_sync = info->stack_ptr = NULL;
71 }
72 else
73 {
74 info->stack_ptr = &arg;
75 debug_printf ("thread '%s', id %p, stack_ptr %p", info->name (), info->id, info->stack_ptr);
76 if (!info->ev)
77 {
78 info->ev = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
79 info->thread_sync = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
80 }
81 }
82
83 while (1)
84 {
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
91 else
92 {
93 if (exiting)
94 {
95 info->inuse = false; // FIXME: Do we need this?
96 return 0;
97 }
98
99 info->callfunc (false);
100
101 HANDLE notify = info->notify_detached;
102 /* If func is NULL, the above function has set that to indicate
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
105 what it is doing. */
106 if (!info->func)
107 info->release (false);
108 else
109 {
110 #ifdef DEBUGGING
111 info->func = NULL; // catch erroneous activation
112 info->__oldname = info->__name;
113 #endif
114 info->__name = NULL;
115 SetEvent (info->ev);
116 }
117 if (notify)
118 SetEvent (notify);
119 }
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 }
128 }
129 }
130
131 /* Overflow stub called by cygthread constructor. Calls specified function
132 and then exits the thread. */
133 DWORD WINAPI
134 cygthread::simplestub (VOID *arg)
135 {
136 cygthread *info = (cygthread *) arg;
137 _my_tls._ctinfo = info;
138 info->stack_ptr = &arg;
139 HANDLE notify = info->notify_detached;
140 info->callfunc (true);
141 if (notify)
142 SetEvent (notify);
143 return 0;
144 }
145
146 /* Start things going. Called from dll_crt0_1. */
147 void
148 cygthread::init ()
149 {
150 main_thread_id = GetCurrentThreadId ();
151 }
152
153 cygthread *
154 cygthread::freerange ()
155 {
156 cygthread *self = (cygthread *) calloc (1, sizeof (*self));
157 self->is_freerange = true;
158 self->inuse = 1;
159 return self;
160 }
161
162 void * cygthread::operator
163 new (size_t)
164 {
165 cygthread *info;
166
167 /* Search the threads array for an empty slot to use */
168 for (info = threads; info < threads + NTHREADS; info++)
169 if (!InterlockedExchange (&info->inuse, 1))
170 {
171 /* available */
172 #ifdef DEBUGGING
173 if (info->__name)
174 api_fatal ("name not NULL? %s, id %p, i %d", info->__name, info->id, info - threads);
175 #endif
176 goto out;
177 }
178
179 #ifdef DEBUGGING
180 if (!getenv ("CYGWIN_FREERANGE_NOCHECK"))
181 api_fatal ("overflowed cygwin thread pool");
182 else
183 thread_printf ("overflowed cygwin thread pool");
184 #endif
185
186 info = freerange (); /* exhausted thread pool */
187
188 out:
189 return info;
190 }
191
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
203 void
204 cygthread::create ()
205 {
206 thread_printf ("name %s, id %p, this %p", __name, id, this);
207 HANDLE htobe;
208 if (h)
209 {
210 if (ev)
211 ResetEvent (ev);
212 while (!thread_sync)
213 yield ();
214 SetEvent (thread_sync);
215 thread_printf ("activated name '%s', thread_sync %p for id %p", __name, thread_sync, id);
216 htobe = h;
217 }
218 else
219 {
220 stack_ptr = NULL;
221 htobe = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
222 this, 0, &id);
223 if (!htobe)
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);
226 #ifdef DEBUGGING
227 terminated = false;
228 #endif
229 }
230
231 if (arglen)
232 {
233 while (!ev)
234 yield ();
235 WaitForSingleObject (ev, INFINITE);
236 ResetEvent (ev);
237 }
238 h = htobe;
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
260 if (res)
261 /* ok */;
262 else if (!_main_tls)
263 res = "main";
264 else
265 {
266 __small_sprintf (_my_tls.locals.unknown_thread_name, "unknown (%p)", tid);
267 res = _my_tls.locals.unknown_thread_name;
268 }
269 return res;
270 }
271
272 cygthread::operator
273 HANDLE ()
274 {
275 while (!ev)
276 yield ();
277 return ev;
278 }
279
280 void
281 cygthread::release (bool nuke_h)
282 {
283 if (nuke_h)
284 h = NULL;
285 #ifdef DEBUGGING
286 __oldname = __name;
287 debug_printf ("released thread '%s'", __oldname);
288 #endif
289 __name = NULL;
290 func = NULL;
291 /* Must be last */
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
298 }
299
300 /* Forcibly terminate a thread. */
301 bool
302 cygthread::terminate_thread ()
303 {
304 bool terminated = true;
305 debug_printf ("thread '%s', id %p, inuse %d, stack_ptr %p", __name, id, inuse, stack_ptr);
306 while (inuse && !stack_ptr)
307 yield ();
308
309 if (!inuse)
310 goto force_notterminated;
311
312 TerminateThread (h, 0);
313 WaitForSingleObject (h, INFINITE);
314 CloseHandle (h);
315
316 if (!inuse || exiting)
317 goto force_notterminated;
318
319 if (ev && !(terminated = !IsEventSignalled (ev)))
320 ResetEvent (ev);
321
322 MEMORY_BASIC_INFORMATION m;
323 memset (&m, 0, sizeof (m));
324 VirtualQuery (stack_ptr, &m, sizeof m);
325
326 if (!m.RegionSize)
327 system_printf ("m.RegionSize 0? stack_ptr %p", stack_ptr);
328 else if (!VirtualFree (m.AllocationBase, 0, MEM_RELEASE))
329 debug_printf ("VirtualFree of allocation base %p<%p> failed, %E",
330 stack_ptr, m.AllocationBase);
331
332 if (is_freerange)
333 free (this);
334 else
335 {
336 #ifdef DEBUGGING
337 terminated = true;
338 #endif
339 release (true);
340 }
341
342 goto out;
343
344 force_notterminated:
345 terminated = false;
346 out:
347 return terminated;
348 }
349
350 /* Detach the cygthread from the current thread. Note that the
351 theory is that cygthreads are only associated with one thread.
352 So, there should be never be multiple threads doing waits
353 on the same cygthread. */
354 bool
355 cygthread::detach (HANDLE sigwait)
356 {
357 bool signalled = false;
358 bool thread_was_reset = false;
359 if (!inuse)
360 system_printf ("called detach but inuse %d, thread %p?", inuse, id);
361 else
362 {
363 DWORD res;
364
365 if (!sigwait)
366 /* If the caller specified a special handle for notification, wait for that.
367 This assumes that the thread in question is auto releasing. */
368 res = WaitForSingleObject (*this, INFINITE);
369 else
370 {
371 /* Lower our priority and give priority to the read thread */
372 HANDLE hth = GetCurrentThread ();
373 LONG prio = GetThreadPriority (hth);
374 ::SetThreadPriority (hth, THREAD_PRIORITY_BELOW_NORMAL);
375
376 HANDLE w4[2];
377 unsigned n = 2;
378 DWORD howlong = INFINITE;
379 w4[0] = sigwait;
380 w4[1] = signal_arrived;
381 /* For a description of the below loop see the end of this file */
382 for (int i = 0; i < 2; i++)
383 switch (res = WaitForMultipleObjects (n, w4, FALSE, howlong))
384 {
385 case WAIT_OBJECT_0:
386 if (n == 1)
387 howlong = 50;
388 break;
389 case WAIT_OBJECT_0 + 1:
390 n = 1;
391 if (i--)
392 howlong = 50;
393 break;
394 case WAIT_TIMEOUT:
395 break;
396 default:
397 if (!exiting)
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 }
412 break;
413 }
414 /* WAIT_OBJECT_0 means that the thread successfully read something,
415 so wait for the cygthread to "terminate". */
416 if (res == WAIT_OBJECT_0)
417 WaitForSingleObject (*this, INFINITE);
418 else
419 {
420 /* Thread didn't terminate on its own, so maybe we have to
421 do it. */
422 signalled = terminate_thread ();
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;
431 }
432 ::SetThreadPriority (hth, prio);
433 }
434
435 thread_printf ("%s returns %d, id %p", sigwait ? "WFMO" : "WFSO",
436 res, id);
437
438 if (thread_was_reset)
439 /* already handled */;
440 else if (is_freerange)
441 {
442 CloseHandle (h);
443 free (this);
444 }
445 else
446 {
447 ResetEvent (*this);
448 /* Mark the thread as available by setting inuse to zero */
449 InterlockedExchange (&inuse, 0);
450 }
451 }
452 return signalled;
453 }
454
455 void
456 cygthread::terminate ()
457 {
458 exiting = 1;
459 }
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 */
This page took 0.059114 seconds and 5 git commands to generate.