Mercurial > emacs
comparison gc/win32_threads.c @ 51488:5de98dce4bd1
*** empty log message ***
author | Dave Love <fx@gnu.org> |
---|---|
date | Thu, 05 Jun 2003 17:49:22 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
51487:01d68b199093 | 51488:5de98dce4bd1 |
---|---|
1 #if defined(GC_WIN32_THREADS) | |
2 | |
3 #include "private/gc_priv.h" | |
4 | |
5 #ifdef CYGWIN32 | |
6 # include <errno.h> | |
7 | |
8 /* Cygwin-specific forward decls */ | |
9 # undef pthread_create | |
10 # undef pthread_sigmask | |
11 # undef pthread_join | |
12 # undef dlopen | |
13 | |
14 # define DEBUG_CYGWIN_THREADS 0 | |
15 | |
16 GC_bool GC_thr_initialized = FALSE; | |
17 void * GC_start_routine(void * arg); | |
18 void GC_thread_exit_proc(void *arg); | |
19 | |
20 #endif | |
21 | |
22 | |
23 #if 0 | |
24 #define STRICT | |
25 #include <windows.h> | |
26 #endif | |
27 | |
28 #define MAX_THREADS 64 | |
29 | |
30 struct thread_entry { | |
31 LONG in_use; | |
32 DWORD id; | |
33 HANDLE handle; | |
34 void *stack; /* The cold end of the stack. */ | |
35 /* 0 ==> entry not valid. */ | |
36 /* !in_use ==> stack == 0 */ | |
37 CONTEXT context; | |
38 GC_bool suspended; | |
39 | |
40 # ifdef CYGWIN32 | |
41 void *status; /* hold exit value until join in case it's a pointer */ | |
42 pthread_t pthread_id; | |
43 # endif | |
44 | |
45 }; | |
46 | |
47 volatile GC_bool GC_please_stop = FALSE; | |
48 | |
49 volatile struct thread_entry thread_table[MAX_THREADS]; | |
50 | |
51 void GC_push_thread_structures GC_PROTO((void)) | |
52 { | |
53 /* Unlike the other threads implementations, the thread table here */ | |
54 /* contains no pointers to the collectable heap. Thus we have */ | |
55 /* no private structures we need to preserve. */ | |
56 # ifdef CYGWIN32 | |
57 { int i; /* pthreads may keep a pointer in the thread exit value */ | |
58 for (i = 0; i < MAX_THREADS; i++) | |
59 if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1)); | |
60 } | |
61 # endif | |
62 } | |
63 | |
64 void GC_stop_world() | |
65 { | |
66 DWORD thread_id = GetCurrentThreadId(); | |
67 int i; | |
68 | |
69 #ifdef CYGWIN32 | |
70 if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); | |
71 #endif | |
72 | |
73 GC_please_stop = TRUE; | |
74 for (i = 0; i < MAX_THREADS; i++) | |
75 if (thread_table[i].stack != 0 | |
76 && thread_table[i].id != thread_id) { | |
77 # ifdef MSWINCE | |
78 /* SuspendThread will fail if thread is running kernel code */ | |
79 while (SuspendThread(thread_table[i].handle) == (DWORD)-1) | |
80 Sleep(10); | |
81 # else | |
82 /* Apparently the Windows 95 GetOpenFileName call creates */ | |
83 /* a thread that does not properly get cleaned up, and */ | |
84 /* SuspendThread on its descriptor may provoke a crash. */ | |
85 /* This reduces the probability of that event, though it still */ | |
86 /* appears there's a race here. */ | |
87 DWORD exitCode; | |
88 if (GetExitCodeThread(thread_table[i].handle,&exitCode) && | |
89 exitCode != STILL_ACTIVE) { | |
90 thread_table[i].stack = 0; /* prevent stack from being pushed */ | |
91 # ifndef CYGWIN32 | |
92 /* this breaks pthread_join on Cygwin, which is guaranteed to */ | |
93 /* only see user pthreads */ | |
94 thread_table[i].in_use = FALSE; | |
95 CloseHandle(thread_table[i].handle); | |
96 BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT)); | |
97 # endif | |
98 continue; | |
99 } | |
100 if (SuspendThread(thread_table[i].handle) == (DWORD)-1) | |
101 ABORT("SuspendThread failed"); | |
102 # endif | |
103 thread_table[i].suspended = TRUE; | |
104 } | |
105 } | |
106 | |
107 void GC_start_world() | |
108 { | |
109 DWORD thread_id = GetCurrentThreadId(); | |
110 int i; | |
111 for (i = 0; i < MAX_THREADS; i++) | |
112 if (thread_table[i].stack != 0 && thread_table[i].suspended | |
113 && thread_table[i].id != thread_id) { | |
114 if (ResumeThread(thread_table[i].handle) == (DWORD)-1) | |
115 ABORT("ResumeThread failed"); | |
116 thread_table[i].suspended = FALSE; | |
117 } | |
118 GC_please_stop = FALSE; | |
119 } | |
120 | |
121 # ifdef _MSC_VER | |
122 # pragma warning(disable:4715) | |
123 # endif | |
124 ptr_t GC_current_stackbottom() | |
125 { | |
126 DWORD thread_id = GetCurrentThreadId(); | |
127 int i; | |
128 for (i = 0; i < MAX_THREADS; i++) | |
129 if (thread_table[i].stack && thread_table[i].id == thread_id) | |
130 return thread_table[i].stack; | |
131 ABORT("no thread table entry for current thread"); | |
132 } | |
133 # ifdef _MSC_VER | |
134 # pragma warning(default:4715) | |
135 # endif | |
136 | |
137 # ifdef MSWINCE | |
138 /* The VirtualQuery calls below won't work properly on WinCE, but */ | |
139 /* since each stack is restricted to an aligned 64K region of */ | |
140 /* virtual memory we can just take the next lowest multiple of 64K. */ | |
141 # define GC_get_lo_stack_addr(s) \ | |
142 ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000)) | |
143 # else | |
144 static ptr_t GC_get_lo_stack_addr(ptr_t s) | |
145 { | |
146 ptr_t bottom; | |
147 MEMORY_BASIC_INFORMATION info; | |
148 VirtualQuery(s, &info, sizeof(info)); | |
149 do { | |
150 bottom = info.BaseAddress; | |
151 VirtualQuery(bottom - 1, &info, sizeof(info)); | |
152 } while ((info.Protect & PAGE_READWRITE) | |
153 && !(info.Protect & PAGE_GUARD)); | |
154 return(bottom); | |
155 } | |
156 # endif | |
157 | |
158 void GC_push_all_stacks() | |
159 { | |
160 DWORD thread_id = GetCurrentThreadId(); | |
161 int i; | |
162 for (i = 0; i < MAX_THREADS; i++) | |
163 if (thread_table[i].stack) { | |
164 ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack); | |
165 if (thread_table[i].id == thread_id) | |
166 GC_push_all_stack((ptr_t)&i, thread_table[i].stack); | |
167 else { | |
168 thread_table[i].context.ContextFlags | |
169 = (CONTEXT_INTEGER|CONTEXT_CONTROL); | |
170 if (!GetThreadContext(thread_table[i].handle, | |
171 /* cast away volatile qualifier */ | |
172 (LPCONTEXT)&thread_table[i].context)) | |
173 ABORT("GetThreadContext failed"); | |
174 # ifdef I386 | |
175 GC_push_one ((word) thread_table[i].context.Edi); | |
176 GC_push_one ((word) thread_table[i].context.Esi); | |
177 GC_push_one ((word) thread_table[i].context.Ebp); | |
178 GC_push_one ((word) thread_table[i].context.Ebx); | |
179 GC_push_one ((word) thread_table[i].context.Edx); | |
180 GC_push_one ((word) thread_table[i].context.Ecx); | |
181 GC_push_one ((word) thread_table[i].context.Eax); | |
182 if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack | |
183 || thread_table[i].context.Esp < (DWORD)bottom) { | |
184 WARN("Thread stack pointer 0x%lx out of range, pushing everything", | |
185 thread_table[i].context.Esp); | |
186 GC_push_all_stack((char *) bottom, thread_table[i].stack); | |
187 } else { | |
188 GC_push_all_stack((char *) thread_table[i].context.Esp, | |
189 thread_table[i].stack); | |
190 } | |
191 # else | |
192 # ifdef ARM32 | |
193 if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack | |
194 || thread_table[i].context.Sp < (DWORD)bottom) | |
195 ABORT("Thread stack pointer out of range"); | |
196 GC_push_one ((word) thread_table[i].context.R0); | |
197 GC_push_one ((word) thread_table[i].context.R1); | |
198 GC_push_one ((word) thread_table[i].context.R2); | |
199 GC_push_one ((word) thread_table[i].context.R3); | |
200 GC_push_one ((word) thread_table[i].context.R4); | |
201 GC_push_one ((word) thread_table[i].context.R5); | |
202 GC_push_one ((word) thread_table[i].context.R6); | |
203 GC_push_one ((word) thread_table[i].context.R7); | |
204 GC_push_one ((word) thread_table[i].context.R8); | |
205 GC_push_one ((word) thread_table[i].context.R9); | |
206 GC_push_one ((word) thread_table[i].context.R10); | |
207 GC_push_one ((word) thread_table[i].context.R11); | |
208 GC_push_one ((word) thread_table[i].context.R12); | |
209 GC_push_all_stack((char *) thread_table[i].context.Sp, | |
210 thread_table[i].stack); | |
211 # else | |
212 # ifdef SHx | |
213 if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack | |
214 || thread_table[i].context.R15 < (DWORD)bottom) | |
215 ABORT("Thread stack pointer out of range"); | |
216 GC_push_one ((word) thread_table[i].context.R0); | |
217 GC_push_one ((word) thread_table[i].context.R1); | |
218 GC_push_one ((word) thread_table[i].context.R2); | |
219 GC_push_one ((word) thread_table[i].context.R3); | |
220 GC_push_one ((word) thread_table[i].context.R4); | |
221 GC_push_one ((word) thread_table[i].context.R5); | |
222 GC_push_one ((word) thread_table[i].context.R6); | |
223 GC_push_one ((word) thread_table[i].context.R7); | |
224 GC_push_one ((word) thread_table[i].context.R8); | |
225 GC_push_one ((word) thread_table[i].context.R9); | |
226 GC_push_one ((word) thread_table[i].context.R10); | |
227 GC_push_one ((word) thread_table[i].context.R11); | |
228 GC_push_one ((word) thread_table[i].context.R12); | |
229 GC_push_one ((word) thread_table[i].context.R13); | |
230 GC_push_one ((word) thread_table[i].context.R14); | |
231 GC_push_all_stack((char *) thread_table[i].context.R15, | |
232 thread_table[i].stack); | |
233 # else | |
234 # ifdef MIPS | |
235 if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack | |
236 || thread_table[i].context.IntSp < (DWORD)bottom) | |
237 ABORT("Thread stack pointer out of range"); | |
238 GC_push_one ((word) thread_table[i].context.IntAt); | |
239 GC_push_one ((word) thread_table[i].context.IntV0); | |
240 GC_push_one ((word) thread_table[i].context.IntV1); | |
241 GC_push_one ((word) thread_table[i].context.IntA0); | |
242 GC_push_one ((word) thread_table[i].context.IntA1); | |
243 GC_push_one ((word) thread_table[i].context.IntA2); | |
244 GC_push_one ((word) thread_table[i].context.IntA3); | |
245 GC_push_one ((word) thread_table[i].context.IntT0); | |
246 GC_push_one ((word) thread_table[i].context.IntT1); | |
247 GC_push_one ((word) thread_table[i].context.IntT2); | |
248 GC_push_one ((word) thread_table[i].context.IntT3); | |
249 GC_push_one ((word) thread_table[i].context.IntT4); | |
250 GC_push_one ((word) thread_table[i].context.IntT5); | |
251 GC_push_one ((word) thread_table[i].context.IntT6); | |
252 GC_push_one ((word) thread_table[i].context.IntT7); | |
253 GC_push_one ((word) thread_table[i].context.IntS0); | |
254 GC_push_one ((word) thread_table[i].context.IntS1); | |
255 GC_push_one ((word) thread_table[i].context.IntS2); | |
256 GC_push_one ((word) thread_table[i].context.IntS3); | |
257 GC_push_one ((word) thread_table[i].context.IntS4); | |
258 GC_push_one ((word) thread_table[i].context.IntS5); | |
259 GC_push_one ((word) thread_table[i].context.IntS6); | |
260 GC_push_one ((word) thread_table[i].context.IntS7); | |
261 GC_push_one ((word) thread_table[i].context.IntT8); | |
262 GC_push_one ((word) thread_table[i].context.IntT9); | |
263 GC_push_one ((word) thread_table[i].context.IntK0); | |
264 GC_push_one ((word) thread_table[i].context.IntK1); | |
265 GC_push_one ((word) thread_table[i].context.IntS8); | |
266 GC_push_all_stack((char *) thread_table[i].context.IntSp, | |
267 thread_table[i].stack); | |
268 # else | |
269 # ifdef PPC | |
270 if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack | |
271 || thread_table[i].context.Gpr1 < (DWORD)bottom) | |
272 ABORT("Thread stack pointer out of range"); | |
273 GC_push_one ((word) thread_table[i].context.Gpr0); | |
274 /* Gpr1 is stack pointer */ | |
275 /* Gpr2 is global pointer */ | |
276 GC_push_one ((word) thread_table[i].context.Gpr3); | |
277 GC_push_one ((word) thread_table[i].context.Gpr4); | |
278 GC_push_one ((word) thread_table[i].context.Gpr5); | |
279 GC_push_one ((word) thread_table[i].context.Gpr6); | |
280 GC_push_one ((word) thread_table[i].context.Gpr7); | |
281 GC_push_one ((word) thread_table[i].context.Gpr8); | |
282 GC_push_one ((word) thread_table[i].context.Gpr9); | |
283 GC_push_one ((word) thread_table[i].context.Gpr10); | |
284 GC_push_one ((word) thread_table[i].context.Gpr11); | |
285 GC_push_one ((word) thread_table[i].context.Gpr12); | |
286 /* Gpr13 is reserved for the kernel */ | |
287 GC_push_one ((word) thread_table[i].context.Gpr14); | |
288 GC_push_one ((word) thread_table[i].context.Gpr15); | |
289 GC_push_one ((word) thread_table[i].context.Gpr16); | |
290 GC_push_one ((word) thread_table[i].context.Gpr17); | |
291 GC_push_one ((word) thread_table[i].context.Gpr18); | |
292 GC_push_one ((word) thread_table[i].context.Gpr19); | |
293 GC_push_one ((word) thread_table[i].context.Gpr20); | |
294 GC_push_one ((word) thread_table[i].context.Gpr21); | |
295 GC_push_one ((word) thread_table[i].context.Gpr22); | |
296 GC_push_one ((word) thread_table[i].context.Gpr23); | |
297 GC_push_one ((word) thread_table[i].context.Gpr24); | |
298 GC_push_one ((word) thread_table[i].context.Gpr25); | |
299 GC_push_one ((word) thread_table[i].context.Gpr26); | |
300 GC_push_one ((word) thread_table[i].context.Gpr27); | |
301 GC_push_one ((word) thread_table[i].context.Gpr28); | |
302 GC_push_one ((word) thread_table[i].context.Gpr29); | |
303 GC_push_one ((word) thread_table[i].context.Gpr30); | |
304 GC_push_one ((word) thread_table[i].context.Gpr31); | |
305 GC_push_all_stack((char *) thread_table[i].context.Gpr1, | |
306 thread_table[i].stack); | |
307 # else | |
308 # ifdef ALPHA | |
309 if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack | |
310 || thread_table[i].context.IntSp < (DWORD)bottom) | |
311 ABORT("Thread stack pointer out of range"); | |
312 GC_push_one ((word) thread_table[i].context.IntV0); | |
313 GC_push_one ((word) thread_table[i].context.IntT0); | |
314 GC_push_one ((word) thread_table[i].context.IntT1); | |
315 GC_push_one ((word) thread_table[i].context.IntT2); | |
316 GC_push_one ((word) thread_table[i].context.IntT3); | |
317 GC_push_one ((word) thread_table[i].context.IntT4); | |
318 GC_push_one ((word) thread_table[i].context.IntT5); | |
319 GC_push_one ((word) thread_table[i].context.IntT6); | |
320 GC_push_one ((word) thread_table[i].context.IntT7); | |
321 GC_push_one ((word) thread_table[i].context.IntS0); | |
322 GC_push_one ((word) thread_table[i].context.IntS1); | |
323 GC_push_one ((word) thread_table[i].context.IntS2); | |
324 GC_push_one ((word) thread_table[i].context.IntS3); | |
325 GC_push_one ((word) thread_table[i].context.IntS4); | |
326 GC_push_one ((word) thread_table[i].context.IntS5); | |
327 GC_push_one ((word) thread_table[i].context.IntFp); | |
328 GC_push_one ((word) thread_table[i].context.IntA0); | |
329 GC_push_one ((word) thread_table[i].context.IntA1); | |
330 GC_push_one ((word) thread_table[i].context.IntA2); | |
331 GC_push_one ((word) thread_table[i].context.IntA3); | |
332 GC_push_one ((word) thread_table[i].context.IntA4); | |
333 GC_push_one ((word) thread_table[i].context.IntA5); | |
334 GC_push_one ((word) thread_table[i].context.IntT8); | |
335 GC_push_one ((word) thread_table[i].context.IntT9); | |
336 GC_push_one ((word) thread_table[i].context.IntT10); | |
337 GC_push_one ((word) thread_table[i].context.IntT11); | |
338 GC_push_one ((word) thread_table[i].context.IntT12); | |
339 GC_push_one ((word) thread_table[i].context.IntAt); | |
340 GC_push_all_stack((char *) thread_table[i].context.IntSp, | |
341 thread_table[i].stack); | |
342 # else | |
343 --> architecture not supported | |
344 # endif /* !ALPHA */ | |
345 # endif /* !PPC */ | |
346 # endif /* !MIPS */ | |
347 # endif /* !SHx */ | |
348 # endif /* !ARM32 */ | |
349 # endif /* !I386 */ | |
350 } | |
351 } | |
352 } | |
353 | |
354 void GC_get_next_stack(char *start, char **lo, char **hi) | |
355 { | |
356 int i; | |
357 # define ADDR_LIMIT (char *)(-1L) | |
358 char * current_min = ADDR_LIMIT; | |
359 | |
360 for (i = 0; i < MAX_THREADS; i++) { | |
361 char * s = (char *)thread_table[i].stack; | |
362 | |
363 if (0 != s && s > start && s < current_min) { | |
364 current_min = s; | |
365 } | |
366 } | |
367 *hi = current_min; | |
368 if (current_min == ADDR_LIMIT) { | |
369 *lo = ADDR_LIMIT; | |
370 return; | |
371 } | |
372 *lo = GC_get_lo_stack_addr(current_min); | |
373 if (*lo < start) *lo = start; | |
374 } | |
375 | |
376 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) | |
377 | |
378 HANDLE WINAPI GC_CreateThread( | |
379 LPSECURITY_ATTRIBUTES lpThreadAttributes, | |
380 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, | |
381 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) | |
382 { | |
383 return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, | |
384 lpParameter, dwCreationFlags, lpThreadId); | |
385 } | |
386 | |
387 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ | |
388 | |
389 typedef struct { | |
390 HANDLE child_ready_h, parent_ready_h; | |
391 volatile struct thread_entry * entry; | |
392 LPTHREAD_START_ROUTINE start; | |
393 LPVOID param; | |
394 } thread_args; | |
395 | |
396 DWORD WINAPI thread_start(LPVOID arg); | |
397 | |
398 HANDLE WINAPI GC_CreateThread( | |
399 LPSECURITY_ATTRIBUTES lpThreadAttributes, | |
400 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, | |
401 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) | |
402 { | |
403 HANDLE thread_h = NULL; | |
404 HANDLE child_ready_h, parent_ready_h; | |
405 | |
406 int i; | |
407 thread_args args; | |
408 | |
409 /* allocate thread slot */ | |
410 LOCK(); | |
411 for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++) | |
412 ; | |
413 if (i != MAX_THREADS) { | |
414 thread_table[i].in_use = TRUE; | |
415 } | |
416 UNLOCK(); | |
417 | |
418 if (i != MAX_THREADS) { | |
419 | |
420 /* create unnamed unsignalled events */ | |
421 if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) { | |
422 if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) { | |
423 | |
424 /* set up thread arguments */ | |
425 args.child_ready_h = child_ready_h; | |
426 args.parent_ready_h = parent_ready_h; | |
427 args.entry = &thread_table[i]; | |
428 args.start = lpStartAddress; | |
429 args.param = lpParameter; | |
430 | |
431 thread_h = CreateThread(lpThreadAttributes, | |
432 dwStackSize, thread_start, | |
433 &args, | |
434 dwCreationFlags & ~CREATE_SUSPENDED, | |
435 lpThreadId); | |
436 | |
437 if (thread_h) { | |
438 | |
439 /* fill in ID and handle; tell child this is done */ | |
440 thread_table[i].id = *lpThreadId; | |
441 thread_table[i].handle = thread_h; | |
442 SetEvent (parent_ready_h); | |
443 | |
444 /* wait for child to fill in stack and copy args */ | |
445 WaitForSingleObject (child_ready_h, INFINITE); | |
446 | |
447 /* suspend the child if requested */ | |
448 if (dwCreationFlags & CREATE_SUSPENDED) | |
449 SuspendThread (thread_h); | |
450 | |
451 /* let child call given function now (or when resumed) */ | |
452 SetEvent (parent_ready_h); | |
453 | |
454 } else { | |
455 CloseHandle (parent_ready_h); | |
456 } | |
457 } | |
458 } | |
459 | |
460 CloseHandle (child_ready_h); | |
461 | |
462 if (thread_h == NULL) | |
463 thread_table[i].in_use = FALSE; | |
464 | |
465 } else { /* no thread slot found */ | |
466 SetLastError (ERROR_TOO_MANY_TCBS); | |
467 } | |
468 | |
469 return thread_h; | |
470 } | |
471 | |
472 static DWORD WINAPI thread_start(LPVOID arg) | |
473 { | |
474 DWORD ret = 0; | |
475 thread_args args = *(thread_args *)arg; | |
476 | |
477 /* wait for parent to fill in ID and handle */ | |
478 WaitForSingleObject (args.parent_ready_h, INFINITE); | |
479 ResetEvent (args.parent_ready_h); | |
480 | |
481 /* fill in stack; tell parent this is done */ | |
482 args.entry->stack = GC_get_stack_base(); | |
483 SetEvent (args.child_ready_h); | |
484 | |
485 /* wait for parent to tell us to go (in case it needs to suspend us) */ | |
486 WaitForSingleObject (args.parent_ready_h, INFINITE); | |
487 CloseHandle (args.parent_ready_h); | |
488 | |
489 /* Clear the thread entry even if we exit with an exception. */ | |
490 /* This is probably pointless, since an uncaught exception is */ | |
491 /* supposed to result in the process being killed. */ | |
492 #ifndef __GNUC__ | |
493 __try { | |
494 #endif /* __GNUC__ */ | |
495 ret = args.start (args.param); | |
496 #ifndef __GNUC__ | |
497 } __finally { | |
498 #endif /* __GNUC__ */ | |
499 LOCK(); | |
500 args.entry->stack = 0; | |
501 args.entry->in_use = FALSE; | |
502 /* cast away volatile qualifier */ | |
503 BZERO((void *) &args.entry->context, sizeof(CONTEXT)); | |
504 UNLOCK(); | |
505 #ifndef __GNUC__ | |
506 } | |
507 #endif /* __GNUC__ */ | |
508 | |
509 return ret; | |
510 } | |
511 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */ | |
512 | |
513 #ifdef MSWINCE | |
514 | |
515 typedef struct { | |
516 HINSTANCE hInstance; | |
517 HINSTANCE hPrevInstance; | |
518 LPWSTR lpCmdLine; | |
519 int nShowCmd; | |
520 } main_thread_args; | |
521 | |
522 DWORD WINAPI main_thread_start(LPVOID arg); | |
523 | |
524 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | |
525 LPWSTR lpCmdLine, int nShowCmd) | |
526 { | |
527 DWORD exit_code = 1; | |
528 | |
529 main_thread_args args = { | |
530 hInstance, hPrevInstance, lpCmdLine, nShowCmd | |
531 }; | |
532 HANDLE thread_h; | |
533 DWORD thread_id; | |
534 | |
535 /* initialize everything */ | |
536 InitializeCriticalSection(&GC_allocate_ml); | |
537 GC_init(); | |
538 | |
539 /* start the main thread */ | |
540 thread_h = GC_CreateThread( | |
541 NULL, 0, main_thread_start, &args, 0, &thread_id); | |
542 | |
543 if (thread_h != NULL) | |
544 { | |
545 WaitForSingleObject (thread_h, INFINITE); | |
546 GetExitCodeThread (thread_h, &exit_code); | |
547 CloseHandle (thread_h); | |
548 } | |
549 | |
550 GC_deinit(); | |
551 DeleteCriticalSection(&GC_allocate_ml); | |
552 | |
553 return (int) exit_code; | |
554 } | |
555 | |
556 DWORD WINAPI main_thread_start(LPVOID arg) | |
557 { | |
558 main_thread_args * args = (main_thread_args *) arg; | |
559 | |
560 return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance, | |
561 args->lpCmdLine, args->nShowCmd); | |
562 } | |
563 | |
564 # else /* !MSWINCE */ | |
565 | |
566 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); | |
567 | |
568 /* threadAttach/threadDetach routines used by both CYGWIN and DLL implementation, | |
569 since both recieve explicit notification on thread creation/destruction | |
570 */ | |
571 void threadAttach() { | |
572 int i; | |
573 /* It appears to be unsafe to acquire a lock here, since this */ | |
574 /* code is apparently not preeemptible on some systems. */ | |
575 /* (This is based on complaints, not on Microsoft's official */ | |
576 /* documentation, which says this should perform "only simple */ | |
577 /* inititalization tasks".) */ | |
578 /* Hence we make do with nonblocking synchronization. */ | |
579 | |
580 /* The following should be a noop according to the win32 */ | |
581 /* documentation. There is empirical evidence that it */ | |
582 /* isn't. - HB */ | |
583 # if defined(MPROTECT_VDB) | |
584 if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); | |
585 # endif | |
586 /* cast away volatile qualifier */ | |
587 for (i = 0; InterlockedExchange((LONG*)&thread_table[i].in_use,1) != 0; i++) { | |
588 /* Compare-and-swap would make this cleaner, but that's not */ | |
589 /* supported before Windows 98 and NT 4.0. In Windows 2000, */ | |
590 /* InterlockedExchange is supposed to be replaced by */ | |
591 /* InterlockedExchangePointer, but that's not really what I */ | |
592 /* want here. */ | |
593 if (i == MAX_THREADS - 1) | |
594 ABORT("too many threads"); | |
595 } | |
596 thread_table[i].id = GetCurrentThreadId(); | |
597 # ifdef CYGWIN32 | |
598 thread_table[i].pthread_id = pthread_self(); | |
599 # endif | |
600 if (!DuplicateHandle(GetCurrentProcess(), | |
601 GetCurrentThread(), | |
602 GetCurrentProcess(), | |
603 (HANDLE*)&thread_table[i].handle, | |
604 0, | |
605 0, | |
606 DUPLICATE_SAME_ACCESS)) { | |
607 DWORD last_error = GetLastError(); | |
608 GC_printf1("Last error code: %lx\n", last_error); | |
609 ABORT("DuplicateHandle failed"); | |
610 } | |
611 thread_table[i].stack = GC_get_stack_base(); | |
612 if (thread_table[i].stack == NULL) | |
613 ABORT("Failed to find stack base in threadAttach"); | |
614 /* If this thread is being created while we are trying to stop */ | |
615 /* the world, wait here. Hopefully this can't happen on any */ | |
616 /* systems that don't allow us to block here. */ | |
617 while (GC_please_stop) Sleep(20); | |
618 } | |
619 | |
620 void threadDetach(DWORD thread_id) { | |
621 int i; | |
622 | |
623 LOCK(); | |
624 for (i = 0; | |
625 i < MAX_THREADS && | |
626 !thread_table[i].in_use || thread_table[i].id != thread_id; | |
627 i++) {} | |
628 if (i >= MAX_THREADS ) { | |
629 WARN("thread %ld not found on detach", (GC_word)thread_id); | |
630 } | |
631 else { | |
632 thread_table[i].stack = 0; | |
633 thread_table[i].in_use = FALSE; | |
634 CloseHandle(thread_table[i].handle); | |
635 /* cast away volatile qualifier */ | |
636 BZERO((void *)&thread_table[i].context, sizeof(CONTEXT)); | |
637 } | |
638 UNLOCK(); | |
639 } | |
640 | |
641 #ifdef CYGWIN32 | |
642 | |
643 /* Called by GC_init() - we hold the allocation lock. */ | |
644 void GC_thr_init() { | |
645 if (GC_thr_initialized) return; | |
646 GC_thr_initialized = TRUE; | |
647 | |
648 #if 0 | |
649 /* this might already be handled in GC_init... */ | |
650 InitializeCriticalSection(&GC_allocate_ml); | |
651 #endif | |
652 | |
653 /* Add the initial thread, so we can stop it. */ | |
654 threadAttach(); | |
655 } | |
656 | |
657 struct start_info { | |
658 void *(*start_routine)(void *); | |
659 void *arg; | |
660 }; | |
661 | |
662 int GC_pthread_join(pthread_t pthread_id, void **retval) { | |
663 int result; | |
664 int i; | |
665 | |
666 # if DEBUG_CYGWIN_THREADS | |
667 GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(), | |
668 GetCurrentThreadId(), (int)pthread_id); | |
669 # endif | |
670 | |
671 /* Can't do any table lookups here, because thread being joined | |
672 might not have registered itself yet */ | |
673 | |
674 result = pthread_join(pthread_id, retval); | |
675 | |
676 LOCK(); | |
677 for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id; | |
678 i++) { | |
679 if (i == MAX_THREADS - 1) { | |
680 GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id); | |
681 ABORT("thread not found on detach"); | |
682 } | |
683 } | |
684 UNLOCK(); | |
685 threadDetach(thread_table[i].id); | |
686 | |
687 # if DEBUG_CYGWIN_THREADS | |
688 GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n", | |
689 (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id); | |
690 # endif | |
691 | |
692 return result; | |
693 } | |
694 | |
695 /* Cygwin-pthreads calls CreateThread internally, but it's not | |
696 * easily interceptible by us.. | |
697 * so intercept pthread_create instead | |
698 */ | |
699 int | |
700 GC_pthread_create(pthread_t *new_thread, | |
701 const pthread_attr_t *attr, | |
702 void *(*start_routine)(void *), void *arg) { | |
703 int result; | |
704 struct start_info * si; | |
705 | |
706 if (!GC_is_initialized) GC_init(); | |
707 /* make sure GC is initialized (i.e. main thread is attached) */ | |
708 | |
709 /* This is otherwise saved only in an area mmapped by the thread */ | |
710 /* library, which isn't visible to the collector. */ | |
711 si = GC_malloc_uncollectable(sizeof(struct start_info)); | |
712 if (0 == si) return(EAGAIN); | |
713 | |
714 si -> start_routine = start_routine; | |
715 si -> arg = arg; | |
716 | |
717 # if DEBUG_CYGWIN_THREADS | |
718 GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(), | |
719 GetCurrentThreadId); | |
720 # endif | |
721 result = pthread_create(new_thread, attr, GC_start_routine, si); | |
722 | |
723 if (result) { /* failure */ | |
724 GC_free(si); | |
725 } | |
726 | |
727 return(result); | |
728 } | |
729 | |
730 void * GC_start_routine(void * arg) | |
731 { | |
732 struct start_info * si = arg; | |
733 void * result; | |
734 void *(*start)(void *); | |
735 void *start_arg; | |
736 pthread_t pthread_id; | |
737 int i; | |
738 | |
739 # if DEBUG_CYGWIN_THREADS | |
740 GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(), | |
741 GetCurrentThreadId()); | |
742 # endif | |
743 | |
744 /* If a GC occurs before the thread is registered, that GC will */ | |
745 /* ignore this thread. That's fine, since it will block trying to */ | |
746 /* acquire the allocation lock, and won't yet hold interesting */ | |
747 /* pointers. */ | |
748 LOCK(); | |
749 /* We register the thread here instead of in the parent, so that */ | |
750 /* we don't need to hold the allocation lock during pthread_create. */ | |
751 threadAttach(); | |
752 UNLOCK(); | |
753 | |
754 start = si -> start_routine; | |
755 start_arg = si -> arg; | |
756 pthread_id = pthread_self(); | |
757 | |
758 GC_free(si); /* was allocated uncollectable */ | |
759 | |
760 pthread_cleanup_push(GC_thread_exit_proc, pthread_id); | |
761 result = (*start)(start_arg); | |
762 pthread_cleanup_pop(0); | |
763 | |
764 # if DEBUG_CYGWIN_THREADS | |
765 GC_printf2("thread 0x%x(0x%x) returned from start routine.\n", | |
766 (int)pthread_self(),GetCurrentThreadId()); | |
767 # endif | |
768 | |
769 LOCK(); | |
770 for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { | |
771 if (i == MAX_THREADS - 1) | |
772 ABORT("thread not found on exit"); | |
773 } | |
774 thread_table[i].status = result; | |
775 UNLOCK(); | |
776 | |
777 return(result); | |
778 } | |
779 | |
780 void GC_thread_exit_proc(void *arg) | |
781 { | |
782 pthread_t pthread_id = (pthread_t)arg; | |
783 int i; | |
784 | |
785 # if DEBUG_CYGWIN_THREADS | |
786 GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",(int)pthread_self(),GetCurrentThreadId()); | |
787 # endif | |
788 | |
789 LOCK(); | |
790 for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { | |
791 if (i == MAX_THREADS - 1) | |
792 ABORT("thread not found on exit"); | |
793 } | |
794 UNLOCK(); | |
795 | |
796 #if 0 | |
797 /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */ | |
798 thread_table[i].status = ??? | |
799 #endif | |
800 } | |
801 | |
802 /* nothing required here... */ | |
803 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { | |
804 return pthread_sigmask(how, set, oset); | |
805 } | |
806 int GC_pthread_detach(pthread_t thread) { | |
807 return pthread_detach(thread); | |
808 } | |
809 #else | |
810 | |
811 /* | |
812 * We avoid acquiring locks here, since this doesn't seem to be preemptable. | |
813 * Pontus Rydin suggests wrapping the thread start routine instead. | |
814 */ | |
815 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) | |
816 { | |
817 switch (reason) { | |
818 case DLL_PROCESS_ATTACH: | |
819 InitializeCriticalSection(&GC_allocate_ml); | |
820 GC_init(); /* Force initialization before thread attach. */ | |
821 /* fall through */ | |
822 case DLL_THREAD_ATTACH: | |
823 threadAttach(); | |
824 break; | |
825 | |
826 case DLL_THREAD_DETACH: | |
827 threadDetach(GetCurrentThreadId()); | |
828 break; | |
829 | |
830 case DLL_PROCESS_DETACH: | |
831 { | |
832 int i; | |
833 | |
834 LOCK(); | |
835 for (i = 0; i < MAX_THREADS; ++i) | |
836 { | |
837 if (thread_table[i].in_use) | |
838 { | |
839 thread_table[i].stack = 0; | |
840 thread_table[i].in_use = FALSE; | |
841 CloseHandle(thread_table[i].handle); | |
842 BZERO((void *) &thread_table[i].context, sizeof(CONTEXT)); | |
843 } | |
844 } | |
845 UNLOCK(); | |
846 | |
847 GC_deinit(); | |
848 DeleteCriticalSection(&GC_allocate_ml); | |
849 } | |
850 break; | |
851 | |
852 } | |
853 return TRUE; | |
854 } | |
855 #endif /* CYGWIN32 */ | |
856 | |
857 # endif /* !MSWINCE */ | |
858 | |
859 #endif /* GC_WIN32_THREADS */ |