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