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