]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #include "private/gc_priv.h" |
2 | ||
3 | #if defined(GC_WIN32_THREADS) | |
4 | ||
5 | #include <windows.h> | |
6 | ||
7 | #ifdef THREAD_LOCAL_ALLOC | |
8 | # include "private/thread_local_alloc.h" | |
9 | #endif /* THREAD_LOCAL_ALLOC */ | |
10 | ||
11 | /* Allocation lock declarations. */ | |
12 | #if !defined(USE_PTHREAD_LOCKS) | |
13 | # if defined(GC_DLL) | |
14 | __declspec(dllexport) CRITICAL_SECTION GC_allocate_ml; | |
15 | # else | |
16 | CRITICAL_SECTION GC_allocate_ml; | |
17 | # endif | |
18 | DWORD GC_lock_holder = NO_THREAD; | |
19 | /* Thread id for current holder of allocation lock */ | |
20 | #else | |
21 | pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; | |
22 | unsigned long GC_lock_holder = NO_THREAD; | |
23 | #endif | |
24 | ||
25 | #ifdef GC_PTHREADS | |
26 | # include <errno.h> | |
27 | ||
28 | /* GC_DLL should not normally be defined, especially since we often do turn */ | |
29 | /* on THREAD_LOCAL_ALLOC, which is currently incompatible. */ | |
30 | /* It might be possible to get GC_DLL and DllMain-based thread registration */ | |
31 | /* to work with Cygwin, but if you try you are on your own. */ | |
32 | #ifdef GC_DLL | |
33 | # error GC_DLL untested with Cygwin | |
34 | #endif | |
35 | ||
36 | /* Cygwin-specific forward decls */ | |
37 | # undef pthread_create | |
38 | # undef pthread_sigmask | |
39 | # undef pthread_join | |
40 | # undef pthread_detach | |
41 | # undef dlopen | |
42 | ||
43 | # ifdef DEBUG_THREADS | |
44 | # ifdef CYGWIN32 | |
45 | # define DEBUG_CYGWIN_THREADS 1 | |
46 | # define DEBUG_WIN32_PTHREADS 0 | |
47 | # else | |
48 | # define DEBUG_WIN32_PTHREADS 1 | |
49 | # define DEBUG_CYGWIN_THREADS 0 | |
50 | # endif | |
51 | # else | |
52 | # define DEBUG_CYGWIN_THREADS 0 | |
53 | # define DEBUG_WIN32_PTHREADS 0 | |
54 | # endif | |
55 | ||
56 | void * GC_pthread_start(void * arg); | |
57 | void GC_thread_exit_proc(void *arg); | |
58 | ||
59 | # include <pthread.h> | |
60 | ||
61 | #else | |
62 | ||
63 | # ifdef DEBUG_THREADS | |
64 | # define DEBUG_WIN32_THREADS 1 | |
65 | # else | |
66 | # define DEBUG_WIN32_THREADS 0 | |
67 | # endif | |
68 | ||
69 | # undef CreateThread | |
70 | # undef ExitThread | |
71 | # undef _beginthreadex | |
72 | # undef _endthreadex | |
73 | # undef _beginthread | |
74 | # ifdef DEBUG_THREADS | |
75 | # define DEBUG_WIN32_THREADS 1 | |
76 | # else | |
77 | # define DEBUG_WIN32_THREADS 0 | |
78 | # endif | |
79 | ||
80 | # include <process.h> /* For _beginthreadex, _endthreadex */ | |
81 | ||
82 | #endif | |
83 | ||
84 | #if defined(GC_DLL) && !defined(MSWINCE) | |
85 | static GC_bool GC_win32_dll_threads = FALSE; | |
86 | /* This code operates in two distinct modes, depending on */ | |
87 | /* the setting of GC_win32_dll_threads. If */ | |
88 | /* GC_win32_dll_threads is set, all threads in the process */ | |
89 | /* are implicitly registered with the GC by DllMain. */ | |
90 | /* No explicit registration is required, and attempts at */ | |
91 | /* explicit registration are ignored. This mode is */ | |
92 | /* very different from the Posix operation of the collector. */ | |
93 | /* In this mode access to the thread table is lock-free. */ | |
94 | /* Hence there is a static limit on the number of threads. */ | |
95 | ||
96 | /* If GC_win32_dll_threads is FALSE, or the collector is */ | |
97 | /* built without GC_DLL defined, things operate in a way */ | |
98 | /* that is very similar to Posix platforms, and new threads */ | |
99 | /* must be registered with the collector, e.g. by using */ | |
100 | /* preprocessor-based interception of the thread primitives. */ | |
101 | /* In this case, we use a real data structure for the thread */ | |
102 | /* table. Note that there is no equivalent of linker-based */ | |
103 | /* call interception, since we don't have ELF-like */ | |
104 | /* facilities. The Windows analog appears to be "API */ | |
105 | /* hooking", which really seems to be a standard way to */ | |
106 | /* do minor binary rewriting (?). I'd prefer not to have */ | |
107 | /* the basic collector rely on such facilities, but an */ | |
108 | /* optional package that intercepts thread calls this way */ | |
109 | /* would probably be nice. */ | |
110 | ||
111 | /* GC_win32_dll_threads must be set at initialization time, */ | |
112 | /* i.e. before any collector or thread calls. We make it a */ | |
113 | /* "dynamic" option only to avoid multiple library versions. */ | |
114 | #else | |
115 | # define GC_win32_dll_threads FALSE | |
116 | #endif | |
117 | ||
118 | /* We have two versions of the thread table. Which one */ | |
119 | /* we us depends on whether or not GC_win32_dll_threads */ | |
120 | /* is set. Note that before initialization, we don't */ | |
121 | /* add any entries to either table, even if DllMain is */ | |
122 | /* called. The main thread will be added on */ | |
123 | /* initialization. */ | |
124 | ||
125 | /* The type of the first argument to InterlockedExchange. */ | |
126 | /* Documented to be LONG volatile *, but at least gcc likes */ | |
127 | /* this better. */ | |
128 | typedef LONG * IE_t; | |
129 | ||
130 | GC_bool GC_thr_initialized = FALSE; | |
131 | ||
132 | GC_bool GC_need_to_lock = FALSE; | |
133 | ||
134 | static GC_bool parallel_initialized = FALSE; | |
135 | ||
136 | void GC_init_parallel(void); | |
137 | ||
138 | #ifdef GC_DLL | |
139 | /* Turn on GC_win32_dll_threads */ | |
140 | GC_API void GC_use_DllMain(void) | |
141 | { | |
142 | # ifdef THREAD_LOCAL_ALLOC | |
143 | ABORT("Cannot use thread local allocation with DllMain-based " | |
144 | "thread registration."); | |
145 | /* Thread-local allocation really wants to lock at thread */ | |
146 | /* entry and exit. */ | |
147 | # endif | |
148 | GC_ASSERT(!parallel_initialized); | |
149 | GC_win32_dll_threads = TRUE; | |
150 | } | |
151 | #else | |
152 | GC_API void GC_use_DllMain(void) | |
153 | { | |
154 | ABORT("GC not configured as DLL"); | |
155 | } | |
156 | #endif | |
157 | ||
158 | DWORD GC_main_thread = 0; | |
159 | ||
160 | struct GC_Thread_Rep { | |
161 | union { | |
162 | AO_t tm_in_use; /* Updated without lock. */ | |
163 | /* We assert that unused */ | |
164 | /* entries have invalid ids of */ | |
165 | /* zero and zero stack fields. */ | |
166 | /* Used only with GC_win32_dll_threads. */ | |
167 | struct GC_Thread_Rep * tm_next; | |
168 | /* Hash table link without */ | |
169 | /* GC_win32_dll_threads. */ | |
170 | /* More recently allocated threads */ | |
171 | /* with a given pthread id come */ | |
172 | /* first. (All but the first are */ | |
173 | /* guaranteed to be dead, but we may */ | |
174 | /* not yet have registered the join.) */ | |
175 | } table_management; | |
176 | # define in_use table_management.tm_in_use | |
177 | # define next table_management.tm_next | |
178 | DWORD id; | |
179 | HANDLE handle; | |
180 | ptr_t stack_base; /* The cold end of the stack. */ | |
181 | /* 0 ==> entry not valid. */ | |
182 | /* !in_use ==> stack_base == 0 */ | |
183 | GC_bool suspended; | |
184 | ||
185 | # ifdef GC_PTHREADS | |
186 | void *status; /* hold exit value until join in case it's a pointer */ | |
187 | pthread_t pthread_id; | |
188 | short flags; /* Protected by GC lock. */ | |
189 | # define FINISHED 1 /* Thread has exited. */ | |
190 | # define DETACHED 2 /* Thread is intended to be detached. */ | |
191 | # define KNOWN_FINISHED(t) (((t) -> flags) & FINISHED) | |
192 | # else | |
193 | # define KNOWN_FINISHED(t) 0 | |
194 | # endif | |
195 | # ifdef THREAD_LOCAL_ALLOC | |
196 | struct thread_local_freelists tlfs; | |
197 | # endif | |
198 | }; | |
199 | ||
200 | typedef struct GC_Thread_Rep * GC_thread; | |
201 | typedef volatile struct GC_Thread_Rep * GC_vthread; | |
202 | ||
203 | /* | |
204 | * We assumed that volatile ==> memory ordering, at least among | |
205 | * volatiles. This code should consistently use atomic_ops. | |
206 | */ | |
207 | ||
208 | volatile GC_bool GC_please_stop = FALSE; | |
209 | ||
210 | /* | |
211 | * We track thread attachments while the world is supposed to be stopped. | |
212 | * Unfortunately, we can't stop them from starting, since blocking in | |
213 | * DllMain seems to cause the world to deadlock. Thus we have to recover | |
214 | * If we notice this in the middle of marking. | |
215 | */ | |
216 | ||
217 | AO_t GC_attached_thread = FALSE; | |
218 | /* Return TRUE if an thread was attached since we last asked or */ | |
219 | /* since GC_attached_thread was explicitly reset. */ | |
220 | GC_bool GC_started_thread_while_stopped(void) | |
221 | { | |
222 | AO_t result; | |
223 | ||
224 | if (GC_win32_dll_threads) { | |
225 | AO_nop_full(); /* Prior heap reads need to complete earlier. */ | |
226 | result = AO_load(&GC_attached_thread); | |
227 | if (result) { | |
228 | AO_store(&GC_attached_thread, FALSE); | |
229 | } | |
230 | return ((GC_bool)result); | |
231 | } else { | |
232 | return FALSE; | |
233 | } | |
234 | } | |
235 | ||
236 | /* Thread table used if GC_win32_dll_threads is set. */ | |
237 | /* This is a fixed size array. */ | |
238 | /* Since we use runtime conditionals, both versions */ | |
239 | /* are always defined. */ | |
240 | # ifndef MAX_THREADS | |
241 | # define MAX_THREADS 512 | |
242 | # endif | |
243 | /* Things may get quite slow for large numbers of threads, */ | |
244 | /* since we look them up with sequential search. */ | |
245 | ||
246 | volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS]; | |
247 | ||
248 | volatile LONG GC_max_thread_index = 0; | |
249 | /* Largest index in dll_thread_table */ | |
250 | /* that was ever used. */ | |
251 | ||
252 | /* And now the version used if GC_win32_dll_threads is not set. */ | |
253 | /* This is a chained hash table, with much of the code borrowed */ | |
254 | /* From the Posix implementation. */ | |
255 | # define THREAD_TABLE_SZ 256 /* Must be power of 2 */ | |
256 | GC_thread GC_threads[THREAD_TABLE_SZ]; | |
257 | ||
258 | ||
259 | /* Add a thread to GC_threads. We assume it wasn't already there. */ | |
260 | /* Caller holds allocation lock. */ | |
261 | /* Unlike the pthreads version, the id field is set by the caller. */ | |
262 | GC_thread GC_new_thread(DWORD id) | |
263 | { | |
264 | word hv = ((word)id) % THREAD_TABLE_SZ; | |
265 | GC_thread result; | |
266 | /* It may not be safe to allocate when we register the first thread. */ | |
267 | static struct GC_Thread_Rep first_thread; | |
268 | static GC_bool first_thread_used = FALSE; | |
269 | ||
270 | GC_ASSERT(I_HOLD_LOCK()); | |
271 | if (!first_thread_used) { | |
272 | result = &first_thread; | |
273 | first_thread_used = TRUE; | |
274 | } else { | |
275 | GC_ASSERT(!GC_win32_dll_threads); | |
276 | result = (struct GC_Thread_Rep *) | |
277 | GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL); | |
278 | # ifdef GC_PTHREADS | |
279 | /* result can be NULL -> segfault */ | |
280 | GC_ASSERT(result -> flags == 0); | |
281 | # endif | |
282 | } | |
283 | if (result == 0) return(0); | |
284 | /* result -> id = id; Done by caller. */ | |
285 | result -> next = GC_threads[hv]; | |
286 | GC_threads[hv] = result; | |
287 | # ifdef GC_PTHREADS | |
288 | GC_ASSERT(result -> flags == 0 /* && result -> thread_blocked == 0 */); | |
289 | # endif | |
290 | return(result); | |
291 | } | |
292 | ||
293 | extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); | |
294 | ||
295 | #if defined(GWW_VDB) && defined(MPROTECT_VDB) | |
296 | extern GC_bool GC_gww_dirty_init(void); | |
297 | /* Defined in os_dep.c. Returns TRUE if GetWriteWatch is available. */ | |
298 | /* may be called repeatedly. */ | |
299 | #endif | |
300 | ||
301 | GC_bool GC_in_thread_creation = FALSE; /* Protected by allocation lock. */ | |
302 | ||
303 | /* | |
304 | * This may be called from DllMain, and hence operates under unusual | |
305 | * constraints. In particular, it must be lock-free if GC_win32_dll_threads | |
306 | * is set. Always called from the thread being added. | |
307 | * If GC_win32_dll_threads is not set, we already hold the allocation lock, | |
308 | * except possibly during single-threaded start-up code. | |
309 | */ | |
310 | static GC_thread GC_register_my_thread_inner(struct GC_stack_base *sb, | |
311 | DWORD thread_id) | |
312 | { | |
313 | GC_vthread me; | |
314 | ||
315 | /* The following should be a noop according to the win32 */ | |
316 | /* documentation. There is empirical evidence that it */ | |
317 | /* isn't. - HB */ | |
318 | # if defined(MPROTECT_VDB) | |
319 | # if defined(GWW_VDB) | |
320 | if (GC_incremental && !GC_gww_dirty_init()) | |
321 | SetUnhandledExceptionFilter(GC_write_fault_handler); | |
322 | # else | |
323 | if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); | |
324 | # endif | |
325 | # endif | |
326 | ||
327 | if (GC_win32_dll_threads) { | |
328 | int i; | |
329 | /* It appears to be unsafe to acquire a lock here, since this */ | |
330 | /* code is apparently not preeemptible on some systems. */ | |
331 | /* (This is based on complaints, not on Microsoft's official */ | |
332 | /* documentation, which says this should perform "only simple */ | |
333 | /* initialization tasks".) */ | |
334 | /* Hence we make do with nonblocking synchronization. */ | |
335 | /* It has been claimed that DllMain is really only executed with */ | |
336 | /* a particular system lock held, and thus careful use of locking */ | |
337 | /* around code that doesn't call back into the system libraries */ | |
338 | /* might be OK. But this hasn't been tested across all win32 */ | |
339 | /* variants. */ | |
340 | /* cast away volatile qualifier */ | |
341 | for (i = 0; InterlockedExchange((IE_t)&dll_thread_table[i].in_use,1) != 0; | |
342 | i++) { | |
343 | /* Compare-and-swap would make this cleaner, but that's not */ | |
344 | /* supported before Windows 98 and NT 4.0. In Windows 2000, */ | |
345 | /* InterlockedExchange is supposed to be replaced by */ | |
346 | /* InterlockedExchangePointer, but that's not really what I */ | |
347 | /* want here. */ | |
348 | /* FIXME: We should eventually declare Win95 dead and use AO_ */ | |
349 | /* primitives here. */ | |
350 | if (i == MAX_THREADS - 1) | |
351 | ABORT("too many threads"); | |
352 | } | |
353 | /* Update GC_max_thread_index if necessary. The following is safe, */ | |
354 | /* and unlike CompareExchange-based solutions seems to work on all */ | |
355 | /* Windows95 and later platforms. */ | |
356 | /* Unfortunately, GC_max_thread_index may be temporarily out of */ | |
357 | /* bounds, so readers have to compensate. */ | |
358 | while (i > GC_max_thread_index) { | |
359 | InterlockedIncrement((IE_t)&GC_max_thread_index); | |
360 | } | |
361 | if (GC_max_thread_index >= MAX_THREADS) { | |
362 | /* We overshot due to simultaneous increments. */ | |
363 | /* Setting it to MAX_THREADS-1 is always safe. */ | |
364 | GC_max_thread_index = MAX_THREADS - 1; | |
365 | } | |
366 | me = dll_thread_table + i; | |
367 | } else /* Not using DllMain */ { | |
368 | GC_ASSERT(I_HOLD_LOCK()); | |
369 | GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */ | |
370 | me = GC_new_thread(thread_id); | |
371 | GC_in_thread_creation = FALSE; | |
372 | } | |
373 | # ifdef GC_PTHREADS | |
374 | /* me can be NULL -> segfault */ | |
375 | me -> pthread_id = pthread_self(); | |
376 | # endif | |
377 | ||
378 | if (!DuplicateHandle(GetCurrentProcess(), | |
379 | GetCurrentThread(), | |
380 | GetCurrentProcess(), | |
381 | (HANDLE*)&(me -> handle), | |
382 | 0, | |
383 | 0, | |
384 | DUPLICATE_SAME_ACCESS)) { | |
385 | DWORD last_error = GetLastError(); | |
386 | GC_err_printf("Last error code: %d\n", last_error); | |
387 | ABORT("DuplicateHandle failed"); | |
388 | } | |
389 | me -> stack_base = sb -> mem_base; | |
390 | /* Up until this point, GC_push_all_stacks considers this thread */ | |
391 | /* invalid. */ | |
392 | /* Up until this point, this entry is viewed as reserved but invalid */ | |
393 | /* by GC_delete_thread. */ | |
394 | me -> id = thread_id; | |
395 | # if defined(THREAD_LOCAL_ALLOC) | |
396 | GC_init_thread_local((GC_tlfs)(&(me->tlfs))); | |
397 | # endif | |
398 | if (me -> stack_base == NULL) | |
399 | ABORT("Bad stack base in GC_register_my_thread_inner"); | |
400 | if (GC_win32_dll_threads) { | |
401 | if (GC_please_stop) { | |
402 | AO_store(&GC_attached_thread, TRUE); | |
403 | AO_nop_full(); // Later updates must become visible after this. | |
404 | } | |
405 | /* We'd like to wait here, but can't, since waiting in DllMain */ | |
406 | /* provokes deadlocks. */ | |
407 | /* Thus we force marking to be restarted instead. */ | |
408 | } else { | |
409 | GC_ASSERT(!GC_please_stop); | |
410 | /* Otherwise both we and the thread stopping code would be */ | |
411 | /* holding the allocation lock. */ | |
412 | } | |
413 | return (GC_thread)(me); | |
414 | } | |
415 | ||
416 | /* | |
417 | * GC_max_thread_index may temporarily be larger than MAX_THREADS. | |
418 | * To avoid subscript errors, we check on access. | |
419 | */ | |
420 | #ifdef __GNUC__ | |
421 | __inline__ | |
422 | #endif | |
423 | LONG GC_get_max_thread_index() | |
424 | { | |
425 | LONG my_max = GC_max_thread_index; | |
426 | ||
427 | if (my_max >= MAX_THREADS) return MAX_THREADS-1; | |
428 | return my_max; | |
429 | } | |
430 | ||
431 | /* Return the GC_thread corresponding to a thread id. May be called */ | |
432 | /* without a lock, but should be called in contexts in which the */ | |
433 | /* requested thread cannot be asynchronously deleted, e.g. from the */ | |
434 | /* thread itself. */ | |
435 | /* This version assumes that either GC_win32_dll_threads is set, or */ | |
436 | /* we hold the allocator lock. */ | |
437 | /* Also used (for assertion checking only) from thread_local_alloc.c. */ | |
438 | GC_thread GC_lookup_thread_inner(DWORD thread_id) { | |
439 | if (GC_win32_dll_threads) { | |
440 | int i; | |
441 | LONG my_max = GC_get_max_thread_index(); | |
442 | for (i = 0; | |
443 | i <= my_max && | |
444 | (!AO_load_acquire(&(dll_thread_table[i].in_use)) | |
445 | || dll_thread_table[i].id != thread_id); | |
446 | /* Must still be in_use, since nobody else can store our thread_id. */ | |
447 | i++) {} | |
448 | if (i > my_max) { | |
449 | return 0; | |
450 | } else { | |
451 | return (GC_thread)(dll_thread_table + i); | |
452 | } | |
453 | } else { | |
454 | word hv = ((word)thread_id) % THREAD_TABLE_SZ; | |
455 | register GC_thread p = GC_threads[hv]; | |
456 | ||
457 | GC_ASSERT(I_HOLD_LOCK()); | |
458 | while (p != 0 && p -> id != thread_id) p = p -> next; | |
459 | return(p); | |
460 | } | |
461 | } | |
462 | ||
463 | /* A version of the above that acquires the lock if necessary. Note */ | |
464 | /* that the identically named function for pthreads is different, and */ | |
465 | /* just assumes we hold the lock. */ | |
466 | /* Also used (for assertion checking only) from thread_local_alloc.c. */ | |
467 | static GC_thread GC_lookup_thread(DWORD thread_id) | |
468 | { | |
469 | if (GC_win32_dll_threads) { | |
470 | return GC_lookup_thread_inner(thread_id); | |
471 | } else { | |
472 | GC_thread result; | |
473 | LOCK(); | |
474 | result = GC_lookup_thread_inner(thread_id); | |
475 | UNLOCK(); | |
476 | return result; | |
477 | } | |
478 | } | |
479 | ||
480 | /* If a thread has been joined, but we have not yet */ | |
481 | /* been notified, then there may be more than one thread */ | |
482 | /* in the table with the same win32 id. */ | |
483 | /* This is OK, but we need a way to delete a specific one. */ | |
484 | /* Assumes we hold the allocation lock unless */ | |
485 | /* GC_win32_dll_threads is set. */ | |
486 | /* If GC_win32_dll_threads is set it should be called from the */ | |
487 | /* thread being deleted. */ | |
488 | void GC_delete_gc_thread(GC_vthread gc_id) | |
489 | { | |
490 | if (GC_win32_dll_threads) { | |
491 | /* This is intended to be lock-free. */ | |
492 | /* It is either called synchronously from the thread being deleted, */ | |
493 | /* or by the joining thread. */ | |
494 | /* In this branch asynchronosu changes to *gc_id are possible. */ | |
495 | CloseHandle(gc_id->handle); | |
496 | gc_id -> stack_base = 0; | |
497 | gc_id -> id = 0; | |
498 | # ifdef CYGWIN32 | |
499 | gc_id -> pthread_id = 0; | |
500 | # endif /* CYGWIN32 */ | |
501 | # ifdef GC_WIN32_PTHREADS | |
502 | gc_id -> pthread_id.p = NULL; | |
503 | # endif /* GC_WIN32_PTHREADS */ | |
504 | AO_store_release(&(gc_id->in_use), FALSE); | |
505 | } else { | |
506 | /* Cast away volatile qualifier, since we have lock. */ | |
507 | GC_thread gc_nvid = (GC_thread)gc_id; | |
508 | DWORD id = gc_nvid -> id; | |
509 | word hv = ((word)id) % THREAD_TABLE_SZ; | |
510 | register GC_thread p = GC_threads[hv]; | |
511 | register GC_thread prev = 0; | |
512 | ||
513 | GC_ASSERT(I_HOLD_LOCK()); | |
514 | while (p != gc_nvid) { | |
515 | prev = p; | |
516 | p = p -> next; | |
517 | } | |
518 | if (prev == 0) { | |
519 | GC_threads[hv] = p -> next; | |
520 | } else { | |
521 | prev -> next = p -> next; | |
522 | } | |
523 | GC_INTERNAL_FREE(p); | |
524 | } | |
525 | } | |
526 | ||
527 | /* Delete a thread from GC_threads. We assume it is there. */ | |
528 | /* (The code intentionally traps if it wasn't.) */ | |
529 | /* Assumes we hold the allocation lock unless */ | |
530 | /* GC_win32_dll_threads is set. */ | |
531 | /* If GC_win32_dll_threads is set it should be called from the */ | |
532 | /* thread being deleted. */ | |
533 | void GC_delete_thread(DWORD id) | |
534 | { | |
535 | if (GC_win32_dll_threads) { | |
536 | GC_thread t = GC_lookup_thread_inner(id); | |
537 | ||
538 | if (0 == t) { | |
539 | WARN("Removing nonexistent thread %ld\n", (GC_word)id); | |
540 | } else { | |
541 | GC_delete_gc_thread(t); | |
542 | } | |
543 | } else { | |
544 | word hv = ((word)id) % THREAD_TABLE_SZ; | |
545 | register GC_thread p = GC_threads[hv]; | |
546 | register GC_thread prev = 0; | |
547 | ||
548 | GC_ASSERT(I_HOLD_LOCK()); | |
549 | while (p -> id != id) { | |
550 | prev = p; | |
551 | p = p -> next; | |
552 | } | |
553 | if (prev == 0) { | |
554 | GC_threads[hv] = p -> next; | |
555 | } else { | |
556 | prev -> next = p -> next; | |
557 | } | |
558 | GC_INTERNAL_FREE(p); | |
559 | } | |
560 | } | |
561 | ||
562 | int GC_register_my_thread(struct GC_stack_base *sb) { | |
563 | DWORD t = GetCurrentThreadId(); | |
564 | ||
565 | if (0 == GC_lookup_thread(t)) { | |
566 | /* We lock here, since we want to wait for an ongoing GC. */ | |
567 | LOCK(); | |
568 | GC_register_my_thread_inner(sb, t); | |
569 | UNLOCK(); | |
570 | return GC_SUCCESS; | |
571 | } else { | |
572 | return GC_DUPLICATE; | |
573 | } | |
574 | } | |
575 | ||
576 | int GC_unregister_my_thread(void) | |
577 | { | |
578 | DWORD t = GetCurrentThreadId(); | |
579 | ||
580 | # if defined(THREAD_LOCAL_ALLOC) | |
581 | LOCK(); | |
582 | { | |
583 | GC_thread me = GC_lookup_thread_inner(t); | |
584 | GC_destroy_thread_local(&(me->tlfs)); | |
585 | } | |
586 | UNLOCK(); | |
587 | # endif | |
588 | if (GC_win32_dll_threads) { | |
589 | /* Should we just ignore this? */ | |
590 | GC_delete_thread(t); | |
591 | } else { | |
592 | LOCK(); | |
593 | GC_delete_thread(t); | |
594 | UNLOCK(); | |
595 | } | |
596 | return GC_SUCCESS; | |
597 | } | |
598 | ||
599 | ||
600 | #ifdef GC_PTHREADS | |
601 | ||
602 | /* A quick-and-dirty cache of the mapping between pthread_t */ | |
603 | /* and win32 thread id. */ | |
604 | #define PTHREAD_MAP_SIZE 512 | |
605 | DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE]; | |
606 | #define HASH(pthread_id) ((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE) | |
607 | /* It appears pthread_t is really a pointer type ... */ | |
608 | #define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \ | |
609 | GC_pthread_map_cache[HASH(pthread_id)] = (win32_id); | |
610 | #define GET_PTHREAD_MAP_CACHE(pthread_id) \ | |
611 | GC_pthread_map_cache[HASH(pthread_id)] | |
612 | ||
613 | /* Return a GC_thread corresponding to a given pthread_t. */ | |
614 | /* Returns 0 if it's not there. */ | |
615 | /* We assume that this is only called for pthread ids that */ | |
616 | /* have not yet terminated or are still joinable, and */ | |
617 | /* cannot be concurrently terminated. */ | |
618 | /* Assumes we do NOT hold the allocation lock. */ | |
619 | static GC_thread GC_lookup_pthread(pthread_t id) | |
620 | { | |
621 | if (GC_win32_dll_threads) { | |
622 | int i; | |
623 | LONG my_max = GC_get_max_thread_index(); | |
624 | ||
625 | for (i = 0; | |
626 | i <= my_max && | |
627 | (!AO_load_acquire(&(dll_thread_table[i].in_use)) | |
628 | || THREAD_EQUAL(dll_thread_table[i].pthread_id, id)); | |
629 | /* Must still be in_use, since nobody else can store our thread_id. */ | |
630 | i++); | |
631 | if (i > my_max) return 0; | |
632 | return (GC_thread)(dll_thread_table + i); | |
633 | } else { | |
634 | /* We first try the cache. If that fails, we use a very slow */ | |
635 | /* approach. */ | |
636 | int hv_guess = GET_PTHREAD_MAP_CACHE(id) % THREAD_TABLE_SZ; | |
637 | int hv; | |
638 | GC_thread p; | |
639 | ||
640 | LOCK(); | |
641 | for (p = GC_threads[hv_guess]; 0 != p; p = p -> next) { | |
642 | if (THREAD_EQUAL(p -> pthread_id, id)) | |
643 | goto foundit; | |
644 | } | |
645 | for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) { | |
646 | for (p = GC_threads[hv]; 0 != p; p = p -> next) { | |
647 | if (THREAD_EQUAL(p -> pthread_id, id)) | |
648 | goto foundit; | |
649 | } | |
650 | } | |
651 | p = 0; | |
652 | foundit: | |
653 | UNLOCK(); | |
654 | return p; | |
655 | } | |
656 | } | |
657 | ||
658 | #endif /* GC_PTHREADS */ | |
659 | ||
660 | void GC_push_thread_structures(void) | |
661 | { | |
662 | GC_ASSERT(I_HOLD_LOCK()); | |
663 | if (GC_win32_dll_threads) { | |
664 | /* Unlike the other threads implementations, the thread table here */ | |
665 | /* contains no pointers to the collectable heap. Thus we have */ | |
666 | /* no private structures we need to preserve. */ | |
667 | # ifdef GC_PTHREADS | |
668 | { int i; /* pthreads may keep a pointer in the thread exit value */ | |
669 | LONG my_max = GC_get_max_thread_index(); | |
670 | ||
671 | for (i = 0; i <= my_max; i++) | |
672 | if (dll_thread_table[i].in_use) | |
673 | GC_push_all((ptr_t)&(dll_thread_table[i].status), | |
674 | (ptr_t)(&(dll_thread_table[i].status)+1)); | |
675 | } | |
676 | # endif | |
677 | } else { | |
678 | GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads)); | |
679 | } | |
680 | # if defined(THREAD_LOCAL_ALLOC) | |
681 | GC_push_all((ptr_t)(&GC_thread_key), | |
682 | (ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key)); | |
683 | /* Just in case we ever use our own TLS implementation. */ | |
684 | # endif | |
685 | } | |
686 | ||
687 | /* Suspend the given thread, if it's still active. */ | |
688 | void GC_suspend(GC_thread t) | |
689 | { | |
690 | # ifdef MSWINCE | |
691 | /* SuspendThread will fail if thread is running kernel code */ | |
692 | while (SuspendThread(t -> handle) == (DWORD)-1) | |
693 | Sleep(10); | |
694 | # else | |
695 | /* Apparently the Windows 95 GetOpenFileName call creates */ | |
696 | /* a thread that does not properly get cleaned up, and */ | |
697 | /* SuspendThread on its descriptor may provoke a crash. */ | |
698 | /* This reduces the probability of that event, though it still */ | |
699 | /* appears there's a race here. */ | |
700 | DWORD exitCode; | |
701 | if (GetExitCodeThread(t -> handle, &exitCode) && | |
702 | exitCode != STILL_ACTIVE) { | |
703 | t -> stack_base = 0; /* prevent stack from being pushed */ | |
704 | # ifndef GC_PTHREADS | |
705 | /* this breaks pthread_join on Cygwin, which is guaranteed to */ | |
706 | /* only see user pthreads */ | |
707 | AO_store(&(t -> in_use), FALSE); | |
708 | CloseHandle(t -> handle); | |
709 | # endif | |
710 | return; | |
711 | } | |
712 | if (SuspendThread(t -> handle) == (DWORD)-1) | |
713 | ABORT("SuspendThread failed"); | |
714 | # endif | |
715 | t -> suspended = TRUE; | |
716 | } | |
717 | ||
718 | /* Defined in misc.c */ | |
719 | #ifndef CYGWIN32 | |
720 | extern CRITICAL_SECTION GC_write_cs; | |
721 | #endif | |
722 | ||
723 | void GC_stop_world(void) | |
724 | { | |
725 | DWORD thread_id = GetCurrentThreadId(); | |
726 | int i; | |
727 | ||
728 | if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); | |
729 | GC_ASSERT(I_HOLD_LOCK()); | |
730 | ||
731 | GC_please_stop = TRUE; | |
732 | # ifndef CYGWIN32 | |
733 | EnterCriticalSection(&GC_write_cs); | |
734 | # endif | |
735 | if (GC_win32_dll_threads) { | |
736 | /* Any threads being created during this loop will end up setting */ | |
737 | /* GC_attached_thread when they start. This will force marking to */ | |
738 | /* restart. */ | |
739 | /* This is not ideal, but hopefully correct. */ | |
740 | GC_attached_thread = FALSE; | |
741 | for (i = 0; i <= GC_get_max_thread_index(); i++) { | |
742 | GC_vthread t = dll_thread_table + i; | |
743 | if (t -> stack_base != 0 | |
744 | && t -> id != thread_id) { | |
745 | GC_suspend((GC_thread)t); | |
746 | } | |
747 | } | |
748 | } else { | |
749 | GC_thread t; | |
750 | int i; | |
751 | ||
752 | for (i = 0; i < THREAD_TABLE_SZ; i++) { | |
753 | for (t = GC_threads[i]; t != 0; t = t -> next) { | |
754 | if (t -> stack_base != 0 | |
755 | && !KNOWN_FINISHED(t) | |
756 | && t -> id != thread_id) { | |
757 | GC_suspend(t); | |
758 | } | |
759 | } | |
760 | } | |
761 | } | |
762 | # ifndef CYGWIN32 | |
763 | LeaveCriticalSection(&GC_write_cs); | |
764 | # endif | |
765 | } | |
766 | ||
767 | void GC_start_world(void) | |
768 | { | |
769 | DWORD thread_id = GetCurrentThreadId(); | |
770 | int i; | |
771 | LONG my_max = GC_get_max_thread_index(); | |
772 | ||
773 | GC_ASSERT(I_HOLD_LOCK()); | |
774 | if (GC_win32_dll_threads) { | |
775 | for (i = 0; i <= my_max; i++) { | |
776 | GC_thread t = (GC_thread)(dll_thread_table + i); | |
777 | if (t -> stack_base != 0 && t -> suspended | |
778 | && t -> id != thread_id) { | |
779 | if (ResumeThread(t -> handle) == (DWORD)-1) | |
780 | ABORT("ResumeThread failed"); | |
781 | t -> suspended = FALSE; | |
782 | } | |
783 | } | |
784 | } else { | |
785 | GC_thread t; | |
786 | int i; | |
787 | ||
788 | for (i = 0; i < THREAD_TABLE_SZ; i++) { | |
789 | for (t = GC_threads[i]; t != 0; t = t -> next) { | |
790 | if (t -> stack_base != 0 && t -> suspended | |
791 | && t -> id != thread_id) { | |
792 | if (ResumeThread(t -> handle) == (DWORD)-1) | |
793 | ABORT("ResumeThread failed"); | |
794 | t -> suspended = FALSE; | |
795 | } | |
796 | } | |
797 | } | |
798 | } | |
799 | GC_please_stop = FALSE; | |
800 | } | |
801 | ||
802 | # ifdef MSWINCE | |
803 | /* The VirtualQuery calls below won't work properly on WinCE, but */ | |
804 | /* since each stack is restricted to an aligned 64K region of */ | |
805 | /* virtual memory we can just take the next lowest multiple of 64K. */ | |
806 | # define GC_get_stack_min(s) \ | |
807 | ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000)) | |
808 | # else | |
809 | static ptr_t GC_get_stack_min(ptr_t s) | |
810 | { | |
811 | ptr_t bottom; | |
812 | MEMORY_BASIC_INFORMATION info; | |
813 | VirtualQuery(s, &info, sizeof(info)); | |
814 | do { | |
815 | bottom = info.BaseAddress; | |
816 | VirtualQuery(bottom - 1, &info, sizeof(info)); | |
817 | } while ((info.Protect & PAGE_READWRITE) | |
818 | && !(info.Protect & PAGE_GUARD)); | |
819 | return(bottom); | |
820 | } | |
821 | # endif | |
822 | ||
823 | void GC_push_stack_for(GC_thread thread) | |
824 | { | |
825 | int dummy; | |
826 | ptr_t sp, stack_min; | |
827 | DWORD me = GetCurrentThreadId(); | |
828 | ||
829 | if (thread -> stack_base) { | |
830 | if (thread -> id == me) { | |
831 | sp = (ptr_t) &dummy; | |
832 | } else { | |
833 | CONTEXT context; | |
834 | context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL; | |
835 | if (!GetThreadContext(thread -> handle, &context)) | |
836 | ABORT("GetThreadContext failed"); | |
837 | ||
838 | /* Push all registers that might point into the heap. Frame */ | |
839 | /* pointer registers are included in case client code was */ | |
840 | /* compiled with the 'omit frame pointer' optimisation. */ | |
841 | # define PUSH1(reg) GC_push_one((word)context.reg) | |
842 | # define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2) | |
843 | # define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4) | |
844 | # if defined(I386) | |
845 | PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp); | |
846 | sp = (ptr_t)context.Esp; | |
847 | # elif defined(X86_64) | |
848 | PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi); | |
849 | PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15); | |
850 | sp = (ptr_t)context.Rsp; | |
851 | # elif defined(ARM32) | |
852 | PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11),PUSH1(R12); | |
853 | sp = (ptr_t)context.Sp; | |
854 | # elif defined(SHx) | |
855 | PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11); | |
856 | PUSH2(R12,R13), PUSH1(R14); | |
857 | sp = (ptr_t)context.R15; | |
858 | # elif defined(MIPS) | |
859 | PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0); | |
860 | PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0); | |
861 | PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8); | |
862 | PUSH4(IntT9,IntK0,IntK1,IntS8); | |
863 | sp = (ptr_t)context.IntSp; | |
864 | # elif defined(PPC) | |
865 | PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9); | |
866 | PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18); | |
867 | PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26); | |
868 | PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31); | |
869 | sp = (ptr_t)context.Gpr1; | |
870 | # elif defined(ALPHA) | |
871 | PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6); | |
872 | PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp); | |
873 | PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9); | |
874 | PUSH4(IntT10,IntT11,IntT12,IntAt); | |
875 | sp = (ptr_t)context.IntSp; | |
876 | # else | |
877 | # error "architecture is not supported" | |
878 | # endif | |
879 | } /* ! current thread */ | |
880 | ||
881 | stack_min = GC_get_stack_min(thread->stack_base); | |
882 | ||
883 | if (sp >= stack_min && sp < thread->stack_base) { | |
884 | # if DEBUG_WIN32_PTHREADS || DEBUG_WIN32_THREADS \ | |
885 | || DEBUG_CYGWIN_THREADS | |
886 | GC_printf("Pushing thread from %p to %p for 0x%x from 0x%x\n", | |
887 | sp, thread -> stack_base, thread -> id, me); | |
888 | # endif | |
889 | GC_push_all_stack(sp, thread->stack_base); | |
890 | } else { | |
891 | WARN("Thread stack pointer 0x%lx out of range, pushing everything\n", | |
892 | (unsigned long)(size_t)sp); | |
893 | GC_push_all_stack(stack_min, thread->stack_base); | |
894 | } | |
895 | } /* thread looks live */ | |
896 | } | |
897 | ||
898 | void GC_push_all_stacks(void) | |
899 | { | |
900 | DWORD me = GetCurrentThreadId(); | |
901 | GC_bool found_me = FALSE; | |
902 | size_t nthreads = 0; | |
903 | ||
904 | if (GC_win32_dll_threads) { | |
905 | int i; | |
906 | LONG my_max = GC_get_max_thread_index(); | |
907 | ||
908 | for (i = 0; i <= my_max; i++) { | |
909 | GC_thread t = (GC_thread)(dll_thread_table + i); | |
910 | if (t -> in_use) { | |
911 | ++nthreads; | |
912 | GC_push_stack_for(t); | |
913 | if (t -> id == me) found_me = TRUE; | |
914 | } | |
915 | } | |
916 | } else { | |
917 | GC_thread t; | |
918 | int i; | |
919 | ||
920 | for (i = 0; i < THREAD_TABLE_SZ; i++) { | |
921 | for (t = GC_threads[i]; t != 0; t = t -> next) { | |
922 | ++nthreads; | |
923 | if (!KNOWN_FINISHED(t)) GC_push_stack_for(t); | |
924 | if (t -> id == me) found_me = TRUE; | |
925 | } | |
926 | } | |
927 | } | |
928 | if (GC_print_stats == VERBOSE) { | |
929 | GC_log_printf("Pushed %d thread stacks ", nthreads); | |
930 | if (GC_win32_dll_threads) { | |
931 | GC_log_printf("based on DllMain thread tracking\n"); | |
932 | } else { | |
933 | GC_log_printf("\n"); | |
934 | } | |
935 | } | |
936 | if (!found_me && !GC_in_thread_creation) | |
937 | ABORT("Collecting from unknown thread."); | |
938 | } | |
939 | ||
940 | void GC_get_next_stack(char *start, char **lo, char **hi) | |
941 | { | |
942 | int i; | |
943 | # define ADDR_LIMIT (char *)(-1L) | |
944 | char * current_min = ADDR_LIMIT; | |
945 | ||
946 | if (GC_win32_dll_threads) { | |
947 | LONG my_max = GC_get_max_thread_index(); | |
948 | ||
949 | for (i = 0; i <= my_max; i++) { | |
950 | ptr_t s = (ptr_t)(dll_thread_table[i].stack_base); | |
951 | ||
952 | if (0 != s && s > start && s < current_min) { | |
953 | current_min = s; | |
954 | } | |
955 | } | |
956 | } else { | |
957 | for (i = 0; i < THREAD_TABLE_SZ; i++) { | |
958 | GC_thread t; | |
959 | ||
960 | for (t = GC_threads[i]; t != 0; t = t -> next) { | |
961 | ptr_t s = (ptr_t)(t -> stack_base); | |
962 | ||
963 | if (0 != s && s > start && s < current_min) { | |
964 | current_min = s; | |
965 | } | |
966 | } | |
967 | } | |
968 | } | |
969 | *hi = current_min; | |
970 | if (current_min == ADDR_LIMIT) { | |
971 | *lo = ADDR_LIMIT; | |
972 | return; | |
973 | } | |
974 | *lo = GC_get_stack_min(current_min); | |
975 | if (*lo < start) *lo = start; | |
976 | } | |
977 | ||
978 | #ifndef GC_PTHREADS | |
979 | ||
980 | /* We have no DllMain to take care of new threads. Thus we */ | |
981 | /* must properly intercept thread creation. */ | |
982 | ||
983 | typedef struct { | |
984 | LPTHREAD_START_ROUTINE start; | |
985 | LPVOID param; | |
986 | } thread_args; | |
987 | ||
988 | static DWORD WINAPI thread_start(LPVOID arg); | |
989 | ||
990 | void * GC_win32_start_inner(struct GC_stack_base *sb, LPVOID arg) | |
991 | { | |
992 | void * ret; | |
993 | thread_args *args = (thread_args *)arg; | |
994 | ||
995 | # if DEBUG_WIN32_THREADS | |
996 | GC_printf("thread 0x%x starting...\n", GetCurrentThreadId()); | |
997 | # endif | |
998 | ||
999 | GC_register_my_thread(sb); /* This waits for an in-progress GC. */ | |
1000 | ||
1001 | /* Clear the thread entry even if we exit with an exception. */ | |
1002 | /* This is probably pointless, since an uncaught exception is */ | |
1003 | /* supposed to result in the process being killed. */ | |
1004 | #ifndef __GNUC__ | |
1005 | __try { | |
1006 | #endif /* __GNUC__ */ | |
1007 | ret = (void *)(size_t)args->start (args->param); | |
1008 | #ifndef __GNUC__ | |
1009 | } __finally { | |
1010 | #endif /* __GNUC__ */ | |
1011 | GC_unregister_my_thread(); | |
1012 | GC_free(args); | |
1013 | #ifndef __GNUC__ | |
1014 | } | |
1015 | #endif /* __GNUC__ */ | |
1016 | ||
1017 | # if DEBUG_WIN32_THREADS | |
1018 | GC_printf("thread 0x%x returned from start routine.\n", | |
1019 | GetCurrentThreadId()); | |
1020 | # endif | |
1021 | return ret; | |
1022 | } | |
1023 | ||
1024 | DWORD WINAPI GC_win32_start(LPVOID arg) | |
1025 | { | |
1026 | return (DWORD)(size_t)GC_call_with_stack_base(GC_win32_start_inner, arg); | |
1027 | } | |
1028 | ||
1029 | GC_API HANDLE WINAPI GC_CreateThread( | |
1030 | LPSECURITY_ATTRIBUTES lpThreadAttributes, | |
1031 | DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, | |
1032 | LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ) | |
1033 | { | |
1034 | HANDLE thread_h = NULL; | |
1035 | ||
1036 | thread_args *args; | |
1037 | ||
1038 | if (!parallel_initialized) GC_init_parallel(); | |
1039 | /* make sure GC is initialized (i.e. main thread is attached, | |
1040 | tls initialized) */ | |
1041 | ||
1042 | # if DEBUG_WIN32_THREADS | |
1043 | GC_printf("About to create a thread from 0x%x\n", GetCurrentThreadId()); | |
1044 | # endif | |
1045 | if (GC_win32_dll_threads) { | |
1046 | return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, | |
1047 | lpParameter, dwCreationFlags, lpThreadId); | |
1048 | } else { | |
1049 | args = GC_malloc_uncollectable(sizeof(thread_args)); | |
1050 | /* Handed off to and deallocated by child thread. */ | |
1051 | if (0 == args) { | |
1052 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); | |
1053 | return NULL; | |
1054 | } | |
1055 | ||
1056 | /* set up thread arguments */ | |
1057 | args -> start = lpStartAddress; | |
1058 | args -> param = lpParameter; | |
1059 | ||
1060 | GC_need_to_lock = TRUE; | |
1061 | thread_h = CreateThread(lpThreadAttributes, | |
1062 | dwStackSize, GC_win32_start, | |
1063 | args, dwCreationFlags, | |
1064 | lpThreadId); | |
1065 | if( thread_h == 0 ) GC_free( args ); | |
1066 | return thread_h; | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | void WINAPI GC_ExitThread(DWORD dwExitCode) | |
1071 | { | |
1072 | GC_unregister_my_thread(); | |
1073 | ExitThread(dwExitCode); | |
1074 | } | |
1075 | ||
1076 | uintptr_t GC_beginthreadex( | |
1077 | void *security, unsigned stack_size, | |
1078 | unsigned ( __stdcall *start_address )( void * ), | |
1079 | void *arglist, unsigned initflag, unsigned *thrdaddr) | |
1080 | { | |
1081 | uintptr_t thread_h = -1L; | |
1082 | ||
1083 | thread_args *args; | |
1084 | ||
1085 | if (!parallel_initialized) GC_init_parallel(); | |
1086 | /* make sure GC is initialized (i.e. main thread is attached, | |
1087 | tls initialized) */ | |
1088 | # if DEBUG_WIN32_THREADS | |
1089 | GC_printf("About to create a thread from 0x%x\n", GetCurrentThreadId()); | |
1090 | # endif | |
1091 | ||
1092 | if (GC_win32_dll_threads) { | |
1093 | return _beginthreadex(security, stack_size, start_address, | |
1094 | arglist, initflag, thrdaddr); | |
1095 | } else { | |
1096 | args = GC_malloc_uncollectable(sizeof(thread_args)); | |
1097 | /* Handed off to and deallocated by child thread. */ | |
1098 | if (0 == args) { | |
1099 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); | |
1100 | return (uintptr_t)(-1); | |
1101 | } | |
1102 | ||
1103 | /* set up thread arguments */ | |
1104 | args -> start = (LPTHREAD_START_ROUTINE)start_address; | |
1105 | args -> param = arglist; | |
1106 | ||
1107 | GC_need_to_lock = TRUE; | |
1108 | thread_h = _beginthreadex(security, stack_size, | |
1109 | (unsigned (__stdcall *) (void *))GC_win32_start, | |
1110 | args, initflag, thrdaddr); | |
1111 | if( thread_h == 0 ) GC_free( args ); | |
1112 | return thread_h; | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | void GC_endthreadex(unsigned retval) | |
1117 | { | |
1118 | GC_unregister_my_thread(); | |
1119 | _endthreadex(retval); | |
1120 | } | |
1121 | ||
1122 | #endif /* !GC_PTHREADS */ | |
1123 | ||
1124 | #ifdef MSWINCE | |
1125 | ||
1126 | typedef struct { | |
1127 | HINSTANCE hInstance; | |
1128 | HINSTANCE hPrevInstance; | |
1129 | LPWSTR lpCmdLine; | |
1130 | int nShowCmd; | |
1131 | } main_thread_args; | |
1132 | ||
1133 | DWORD WINAPI main_thread_start(LPVOID arg); | |
1134 | ||
1135 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | |
1136 | LPWSTR lpCmdLine, int nShowCmd) | |
1137 | { | |
1138 | DWORD exit_code = 1; | |
1139 | ||
1140 | main_thread_args args = { | |
1141 | hInstance, hPrevInstance, lpCmdLine, nShowCmd | |
1142 | }; | |
1143 | HANDLE thread_h; | |
1144 | DWORD thread_id; | |
1145 | ||
1146 | /* initialize everything */ | |
1147 | GC_init(); | |
1148 | ||
1149 | /* start the main thread */ | |
1150 | thread_h = GC_CreateThread( | |
1151 | NULL, 0, main_thread_start, &args, 0, &thread_id); | |
1152 | ||
1153 | if (thread_h != NULL) | |
1154 | { | |
1155 | WaitForSingleObject (thread_h, INFINITE); | |
1156 | GetExitCodeThread (thread_h, &exit_code); | |
1157 | CloseHandle (thread_h); | |
1158 | } | |
1159 | ||
1160 | GC_deinit(); | |
1161 | DeleteCriticalSection(&GC_allocate_ml); | |
1162 | ||
1163 | return (int) exit_code; | |
1164 | } | |
1165 | ||
1166 | DWORD WINAPI main_thread_start(LPVOID arg) | |
1167 | { | |
1168 | main_thread_args * args = (main_thread_args *) arg; | |
1169 | ||
1170 | return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance, | |
1171 | args->lpCmdLine, args->nShowCmd); | |
1172 | } | |
1173 | ||
1174 | # else /* !MSWINCE */ | |
1175 | ||
1176 | /* Called by GC_init() - we hold the allocation lock. */ | |
1177 | void GC_thr_init(void) { | |
1178 | struct GC_stack_base sb; | |
1179 | int sb_result; | |
1180 | ||
1181 | GC_ASSERT(I_HOLD_LOCK()); | |
1182 | if (GC_thr_initialized) return; | |
1183 | GC_main_thread = GetCurrentThreadId(); | |
1184 | GC_thr_initialized = TRUE; | |
1185 | ||
1186 | /* Add the initial thread, so we can stop it. */ | |
1187 | sb_result = GC_get_stack_base(&sb); | |
1188 | GC_ASSERT(sb_result == GC_SUCCESS); | |
1189 | GC_register_my_thread(&sb); | |
1190 | } | |
1191 | ||
1192 | #ifdef GC_PTHREADS | |
1193 | ||
1194 | struct start_info { | |
1195 | void *(*start_routine)(void *); | |
1196 | void *arg; | |
1197 | GC_bool detached; | |
1198 | }; | |
1199 | ||
1200 | int GC_pthread_join(pthread_t pthread_id, void **retval) { | |
1201 | int result; | |
1202 | int i; | |
1203 | GC_thread joinee; | |
1204 | ||
1205 | # if DEBUG_CYGWIN_THREADS | |
1206 | GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n", | |
1207 | (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id); | |
1208 | # endif | |
1209 | # if DEBUG_WIN32_PTHREADS | |
1210 | GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n", | |
1211 | (int)(pthread_self()).p, GetCurrentThreadId(), pthread_id.p); | |
1212 | # endif | |
1213 | ||
1214 | if (!parallel_initialized) GC_init_parallel(); | |
1215 | /* Thread being joined might not have registered itself yet. */ | |
1216 | /* After the join,thread id may have been recycled. */ | |
1217 | /* FIXME: It would be better if this worked more like */ | |
1218 | /* pthread_support.c. */ | |
1219 | ||
1220 | #ifndef GC_WIN32_PTHREADS | |
1221 | while ((joinee = GC_lookup_pthread(pthread_id)) == 0) Sleep(10); | |
1222 | #endif | |
1223 | ||
1224 | result = pthread_join(pthread_id, retval); | |
1225 | ||
1226 | #ifdef GC_WIN32_PTHREADS | |
1227 | /* win32_pthreads id are unique */ | |
1228 | joinee = GC_lookup_pthread(pthread_id); | |
1229 | #endif | |
1230 | ||
1231 | if (!GC_win32_dll_threads) { | |
1232 | LOCK(); | |
1233 | GC_delete_gc_thread(joinee); | |
1234 | UNLOCK(); | |
1235 | } /* otherwise dllmain handles it. */ | |
1236 | ||
1237 | # if DEBUG_CYGWIN_THREADS | |
1238 | GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n", | |
1239 | (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id); | |
1240 | # endif | |
1241 | # if DEBUG_WIN32_PTHREADS | |
1242 | GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n", | |
1243 | (int)(pthread_self()).p, GetCurrentThreadId(), pthread_id.p); | |
1244 | # endif | |
1245 | ||
1246 | return result; | |
1247 | } | |
1248 | ||
1249 | /* Cygwin-pthreads calls CreateThread internally, but it's not | |
1250 | * easily interceptible by us.. | |
1251 | * so intercept pthread_create instead | |
1252 | */ | |
1253 | int | |
1254 | GC_pthread_create(pthread_t *new_thread, | |
1255 | const pthread_attr_t *attr, | |
1256 | void *(*start_routine)(void *), void *arg) { | |
1257 | int result; | |
1258 | struct start_info * si; | |
1259 | ||
1260 | if (!parallel_initialized) GC_init_parallel(); | |
1261 | /* make sure GC is initialized (i.e. main thread is attached) */ | |
1262 | if (GC_win32_dll_threads) { | |
1263 | return pthread_create(new_thread, attr, start_routine, arg); | |
1264 | } | |
1265 | ||
1266 | /* This is otherwise saved only in an area mmapped by the thread */ | |
1267 | /* library, which isn't visible to the collector. */ | |
1268 | si = GC_malloc_uncollectable(sizeof(struct start_info)); | |
1269 | if (0 == si) return(EAGAIN); | |
1270 | ||
1271 | si -> start_routine = start_routine; | |
1272 | si -> arg = arg; | |
1273 | if (attr != 0 && | |
1274 | pthread_attr_getdetachstate(attr, &si->detached) | |
1275 | == PTHREAD_CREATE_DETACHED) { | |
1276 | si->detached = TRUE; | |
1277 | } | |
1278 | ||
1279 | # if DEBUG_CYGWIN_THREADS | |
1280 | GC_printf("About to create a thread from 0x%x(0x%x)\n", | |
1281 | (int)pthread_self(), GetCurrentThreadId); | |
1282 | # endif | |
1283 | # if DEBUG_WIN32_PTHREADS | |
1284 | GC_printf("About to create a thread from 0x%x(0x%x)\n", | |
1285 | (int)(pthread_self()).p, GetCurrentThreadId()); | |
1286 | # endif | |
1287 | GC_need_to_lock = TRUE; | |
1288 | result = pthread_create(new_thread, attr, GC_pthread_start, si); | |
1289 | ||
1290 | if (result) { /* failure */ | |
1291 | GC_free(si); | |
1292 | } | |
1293 | ||
1294 | return(result); | |
1295 | } | |
1296 | ||
1297 | void * GC_pthread_start_inner(struct GC_stack_base *sb, void * arg) | |
1298 | { | |
1299 | struct start_info * si = arg; | |
1300 | void * result; | |
1301 | void *(*start)(void *); | |
1302 | void *start_arg; | |
1303 | DWORD thread_id = GetCurrentThreadId(); | |
1304 | pthread_t pthread_id = pthread_self(); | |
1305 | GC_thread me; | |
1306 | GC_bool detached; | |
1307 | int i; | |
1308 | ||
1309 | # if DEBUG_CYGWIN_THREADS | |
1310 | GC_printf("thread 0x%x(0x%x) starting...\n",(int)pthread_id, | |
1311 | thread_id); | |
1312 | # endif | |
1313 | # if DEBUG_WIN32_PTHREADS | |
1314 | GC_printf("thread 0x%x(0x%x) starting...\n",(int) pthread_id.p, | |
1315 | thread_id); | |
1316 | # endif | |
1317 | ||
1318 | GC_ASSERT(!GC_win32_dll_threads); | |
1319 | /* If a GC occurs before the thread is registered, that GC will */ | |
1320 | /* ignore this thread. That's fine, since it will block trying to */ | |
1321 | /* acquire the allocation lock, and won't yet hold interesting */ | |
1322 | /* pointers. */ | |
1323 | LOCK(); | |
1324 | /* We register the thread here instead of in the parent, so that */ | |
1325 | /* we don't need to hold the allocation lock during pthread_create. */ | |
1326 | me = GC_register_my_thread_inner(sb, thread_id); | |
1327 | SET_PTHREAD_MAP_CACHE(pthread_id, thread_id); | |
1328 | UNLOCK(); | |
1329 | ||
1330 | start = si -> start_routine; | |
1331 | start_arg = si -> arg; | |
1332 | if (si-> detached) me -> flags |= DETACHED; | |
1333 | me -> pthread_id = pthread_id; | |
1334 | ||
1335 | GC_free(si); /* was allocated uncollectable */ | |
1336 | ||
1337 | pthread_cleanup_push(GC_thread_exit_proc, (void *)me); | |
1338 | result = (*start)(start_arg); | |
1339 | me -> status = result; | |
1340 | pthread_cleanup_pop(1); | |
1341 | ||
1342 | # if DEBUG_CYGWIN_THREADS | |
1343 | GC_printf("thread 0x%x(0x%x) returned from start routine.\n", | |
1344 | (int)pthread_self(),GetCurrentThreadId()); | |
1345 | # endif | |
1346 | # if DEBUG_WIN32_PTHREADS | |
1347 | GC_printf("thread 0x%x(0x%x) returned from start routine.\n", | |
1348 | (int)(pthread_self()).p, GetCurrentThreadId()); | |
1349 | # endif | |
1350 | ||
1351 | return(result); | |
1352 | } | |
1353 | ||
1354 | void * GC_pthread_start(void * arg) | |
1355 | { | |
1356 | return GC_call_with_stack_base(GC_pthread_start_inner, arg); | |
1357 | } | |
1358 | ||
1359 | void GC_thread_exit_proc(void *arg) | |
1360 | { | |
1361 | GC_thread me = (GC_thread)arg; | |
1362 | int i; | |
1363 | ||
1364 | GC_ASSERT(!GC_win32_dll_threads); | |
1365 | # if DEBUG_CYGWIN_THREADS | |
1366 | GC_printf("thread 0x%x(0x%x) called pthread_exit().\n", | |
1367 | (int)pthread_self(),GetCurrentThreadId()); | |
1368 | # endif | |
1369 | # if DEBUG_WIN32_PTHREADS | |
1370 | GC_printf("thread 0x%x(0x%x) called pthread_exit().\n", | |
1371 | (int)(pthread_self()).p,GetCurrentThreadId()); | |
1372 | # endif | |
1373 | ||
1374 | LOCK(); | |
1375 | # if defined(THREAD_LOCAL_ALLOC) | |
1376 | GC_destroy_thread_local(&(me->tlfs)); | |
1377 | # endif | |
1378 | if (me -> flags & DETACHED) { | |
1379 | GC_delete_thread(GetCurrentThreadId()); | |
1380 | } else { | |
1381 | /* deallocate it as part of join */ | |
1382 | me -> flags |= FINISHED; | |
1383 | } | |
1384 | UNLOCK(); | |
1385 | } | |
1386 | ||
1387 | #ifndef GC_WIN32_PTHREADS | |
1388 | /* win32 pthread does not support sigmask */ | |
1389 | /* nothing required here... */ | |
1390 | int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { | |
1391 | if (!parallel_initialized) GC_init_parallel(); | |
1392 | return pthread_sigmask(how, set, oset); | |
1393 | } | |
1394 | #endif | |
1395 | ||
1396 | int GC_pthread_detach(pthread_t thread) | |
1397 | { | |
1398 | int result; | |
1399 | GC_thread thread_gc_id; | |
1400 | ||
1401 | if (!parallel_initialized) GC_init_parallel(); | |
1402 | LOCK(); | |
1403 | thread_gc_id = GC_lookup_pthread(thread); | |
1404 | UNLOCK(); | |
1405 | result = pthread_detach(thread); | |
1406 | if (result == 0) { | |
1407 | LOCK(); | |
1408 | thread_gc_id -> flags |= DETACHED; | |
1409 | /* Here the pthread thread id may have been recycled. */ | |
1410 | if (thread_gc_id -> flags & FINISHED) { | |
1411 | GC_delete_gc_thread(thread_gc_id); | |
1412 | } | |
1413 | UNLOCK(); | |
1414 | } | |
1415 | return result; | |
1416 | } | |
1417 | ||
1418 | #else /* !GC_PTHREADS */ | |
1419 | ||
1420 | /* | |
1421 | * We avoid acquiring locks here, since this doesn't seem to be preemptable. | |
1422 | * This may run with an uninitialized collector, in which case we don't do much. | |
1423 | * This implies that no threads other than the main one should be created | |
1424 | * with an uninitialized collector. (The alternative of initializing | |
1425 | * the collector here seems dangerous, since DllMain is limited in what it | |
1426 | * can do.) | |
1427 | */ | |
1428 | #ifdef GC_DLL | |
1429 | GC_API BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) | |
1430 | { | |
1431 | struct GC_stack_base sb; | |
1432 | DWORD thread_id; | |
1433 | int sb_result; | |
1434 | static int entry_count = 0; | |
1435 | ||
1436 | if (parallel_initialized && !GC_win32_dll_threads) return TRUE; | |
1437 | ||
1438 | switch (reason) { | |
1439 | case DLL_THREAD_ATTACH: | |
1440 | GC_ASSERT(entry_count == 0 || parallel_initialized); | |
1441 | ++entry_count; /* and fall through: */ | |
1442 | case DLL_PROCESS_ATTACH: | |
1443 | /* This may run with the collector uninitialized. */ | |
1444 | thread_id = GetCurrentThreadId(); | |
1445 | if (parallel_initialized && GC_main_thread != thread_id) { | |
1446 | /* Don't lock here. */ | |
1447 | sb_result = GC_get_stack_base(&sb); | |
1448 | GC_ASSERT(sb_result == GC_SUCCESS); | |
1449 | # ifdef THREAD_LOCAL_ALLOC | |
1450 | ABORT("Cannot initialize thread local cache from DllMain"); | |
1451 | # endif | |
1452 | GC_register_my_thread_inner(&sb, thread_id); | |
1453 | } /* o.w. we already did it during GC_thr_init(), called by GC_init() */ | |
1454 | break; | |
1455 | ||
1456 | case DLL_THREAD_DETACH: | |
1457 | /* We are hopefully running in the context of the exiting thread. */ | |
1458 | GC_ASSERT(parallel_initialized); | |
1459 | if (!GC_win32_dll_threads) return TRUE; | |
1460 | GC_delete_thread(GetCurrentThreadId()); | |
1461 | break; | |
1462 | ||
1463 | case DLL_PROCESS_DETACH: | |
1464 | { | |
1465 | int i; | |
1466 | ||
1467 | if (!GC_win32_dll_threads) return TRUE; | |
1468 | for (i = 0; i <= GC_get_max_thread_index(); ++i) | |
1469 | { | |
1470 | if (AO_load(&(dll_thread_table[i].in_use))) | |
1471 | GC_delete_gc_thread(dll_thread_table + i); | |
1472 | } | |
1473 | ||
1474 | GC_deinit(); | |
1475 | DeleteCriticalSection(&GC_allocate_ml); | |
1476 | } | |
1477 | break; | |
1478 | ||
1479 | } | |
1480 | return TRUE; | |
1481 | } | |
1482 | #endif /* GC_DLL */ | |
1483 | #endif /* !GC_PTHREADS */ | |
1484 | ||
1485 | # endif /* !MSWINCE */ | |
1486 | ||
1487 | /* Perform all initializations, including those that */ | |
1488 | /* may require allocation. */ | |
1489 | /* Called without allocation lock. */ | |
1490 | /* Must be called before a second thread is created. */ | |
1491 | void GC_init_parallel(void) | |
1492 | { | |
1493 | if (parallel_initialized) return; | |
1494 | parallel_initialized = TRUE; | |
1495 | /* GC_init() calls us back, so set flag first. */ | |
1496 | ||
1497 | if (!GC_is_initialized) GC_init(); | |
1498 | if (GC_win32_dll_threads) { | |
1499 | GC_need_to_lock = TRUE; | |
1500 | /* Cannot intercept thread creation. Hence we don't know if other */ | |
1501 | /* threads exist. However, client is not allowed to create other */ | |
1502 | /* threads before collector initialization. Thus it's OK not to */ | |
1503 | /* lock before this. */ | |
1504 | } | |
1505 | /* Initialize thread local free lists if used. */ | |
1506 | # if defined(THREAD_LOCAL_ALLOC) | |
1507 | LOCK(); | |
1508 | GC_init_thread_local(&(GC_lookup_thread(GetCurrentThreadId())->tlfs)); | |
1509 | UNLOCK(); | |
1510 | # endif | |
1511 | } | |
1512 | ||
1513 | #if defined(USE_PTHREAD_LOCKS) | |
1514 | /* Support for pthread locking code. */ | |
1515 | /* Pthread_mutex_try_lock may not win here, */ | |
1516 | /* due to builtinsupport for spinning first? */ | |
1517 | ||
1518 | volatile GC_bool GC_collecting = 0; | |
1519 | /* A hint that we're in the collector and */ | |
1520 | /* holding the allocation lock for an */ | |
1521 | /* extended period. */ | |
1522 | ||
1523 | void GC_lock(void) | |
1524 | { | |
1525 | pthread_mutex_lock(&GC_allocate_ml); | |
1526 | } | |
1527 | #endif /* USE_PTHREAD ... */ | |
1528 | ||
1529 | # if defined(THREAD_LOCAL_ALLOC) | |
1530 | ||
1531 | /* Add thread-local allocation support. Microsoft uses __declspec(thread) */ | |
1532 | ||
1533 | /* We must explicitly mark ptrfree and gcj free lists, since the free */ | |
1534 | /* list links wouldn't otherwise be found. We also set them in the */ | |
1535 | /* normal free lists, since that involves touching less memory than if */ | |
1536 | /* we scanned them normally. */ | |
1537 | void GC_mark_thread_local_free_lists(void) | |
1538 | { | |
1539 | int i; | |
1540 | GC_thread p; | |
1541 | ||
1542 | for (i = 0; i < THREAD_TABLE_SZ; ++i) { | |
1543 | for (p = GC_threads[i]; 0 != p; p = p -> next) { | |
1544 | GC_mark_thread_local_fls_for(&(p->tlfs)); | |
1545 | } | |
1546 | } | |
1547 | } | |
1548 | ||
1549 | #if defined(GC_ASSERTIONS) | |
1550 | /* Check that all thread-local free-lists are completely marked. */ | |
1551 | /* also check that thread-specific-data structures are marked. */ | |
1552 | void GC_check_tls(void) { | |
1553 | int i; | |
1554 | GC_thread p; | |
1555 | ||
1556 | for (i = 0; i < THREAD_TABLE_SZ; ++i) { | |
1557 | for (p = GC_threads[i]; 0 != p; p = p -> next) { | |
1558 | GC_check_tls_for(&(p->tlfs)); | |
1559 | } | |
1560 | } | |
1561 | # if defined(USE_CUSTOM_SPECIFIC) | |
1562 | if (GC_thread_key != 0) | |
1563 | GC_check_tsd_marks(GC_thread_key); | |
1564 | # endif | |
1565 | } | |
1566 | #endif /* GC_ASSERTIONS */ | |
1567 | ||
1568 | #endif /* THREAD_LOCAL_ALLOC ... */ | |
1569 | ||
1570 | #endif /* GC_WIN32_THREADS */ |