14
40
# undef pthread_detach
17
# define DEBUG_CYGWIN_THREADS 0
45
# define DEBUG_CYGWIN_THREADS 1
46
# define DEBUG_WIN32_PTHREADS 0
48
# define DEBUG_WIN32_PTHREADS 1
49
# define DEBUG_CYGWIN_THREADS 0
52
# define DEBUG_CYGWIN_THREADS 0
53
# define DEBUG_WIN32_PTHREADS 0
19
void * GC_start_routine(void * arg);
56
void * GC_pthread_start(void * arg);
20
57
void GC_thread_exit_proc(void *arg);
64
# define DEBUG_WIN32_THREADS 1
66
# define DEBUG_WIN32_THREADS 0
71
# undef _beginthreadex
75
# define DEBUG_WIN32_THREADS 1
77
# define DEBUG_WIN32_THREADS 0
80
# include <process.h> /* For _beginthreadex, _endthreadex */
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. */
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. */
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. */
115
# define GC_win32_dll_threads FALSE
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. */
24
125
/* The type of the first argument to InterlockedExchange. */
25
126
/* Documented to be LONG volatile *, but at least gcc likes */
26
127
/* this better. */
27
128
typedef LONG * IE_t;
30
# define MAX_THREADS 256
32
/* Things may get quite slow for large numbers of threads, */
33
/* since we look them up with sequential search. */
36
130
GC_bool GC_thr_initialized = FALSE;
132
GC_bool GC_need_to_lock = FALSE;
134
static GC_bool parallel_initialized = FALSE;
136
void GC_init_parallel(void);
139
/* Turn on GC_win32_dll_threads */
140
GC_API void GC_use_DllMain(void)
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. */
148
GC_ASSERT(!parallel_initialized);
149
GC_win32_dll_threads = TRUE;
153
GC_API void GC_use_DllMain(void)
155
ABORT("GC not configured as DLL");
38
159
DWORD GC_main_thread = 0;
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. */
161
struct GC_Thread_Rep {
163
AO_t tm_in_use; /* Updated without lock. */
164
/* We assert that unused */
165
/* entries have invalid ids of */
166
/* zero and zero stack fields. */
167
/* Used only with GC_win32_dll_threads. */
168
struct GC_Thread_Rep * tm_next;
169
/* Hash table link without */
170
/* GC_win32_dll_threads. */
171
/* More recently allocated threads */
172
/* with a given pthread id come */
173
/* first. (All but the first are */
174
/* guaranteed to be dead, but we may */
175
/* not yet have registered the join.) */
177
# define in_use table_management.tm_in_use
178
# define next table_management.tm_next
47
181
ptr_t stack_base; /* The cold end of the stack. */
49
183
/* !in_use ==> stack_base == 0 */
50
184
GC_bool suspended;
53
187
void *status; /* hold exit value until join in case it's a pointer */
54
188
pthread_t pthread_id;
55
189
short flags; /* Protected by GC lock. */
56
190
# define FINISHED 1 /* Thread has exited. */
57
191
# define DETACHED 2 /* Thread is intended to be detached. */
192
# define KNOWN_FINISHED(t) (((t) -> flags) & FINISHED)
194
# define KNOWN_FINISHED(t) 0
196
# ifdef THREAD_LOCAL_ALLOC
197
struct thread_local_freelists tlfs;
61
typedef volatile struct GC_thread_Rep * GC_thread;
201
typedef struct GC_Thread_Rep * GC_thread;
202
typedef volatile struct GC_Thread_Rep * GC_vthread;
64
* We generally assume that volatile ==> memory ordering, at least among
205
* We assumed that volatile ==> memory ordering, at least among
206
* volatiles. This code should consistently use atomic_ops.
68
209
volatile GC_bool GC_please_stop = FALSE;
70
volatile struct GC_thread_Rep thread_table[MAX_THREADS];
72
volatile LONG GC_max_thread_index = 0; /* Largest index in thread_table */
73
/* that was ever used. */
212
* We track thread attachments while the world is supposed to be stopped.
213
* Unfortunately, we can't stop them from starting, since blocking in
214
* DllMain seems to cause the world to deadlock. Thus we have to recover
215
* If we notice this in the middle of marking.
218
AO_t GC_attached_thread = FALSE;
219
/* Return TRUE if an thread was attached since we last asked or */
220
/* since GC_attached_thread was explicitly reset. */
221
GC_bool GC_started_thread_while_stopped(void)
225
if (GC_win32_dll_threads) {
226
AO_nop_full(); /* Prior heap reads need to complete earlier. */
227
result = AO_load(&GC_attached_thread);
229
AO_store(&GC_attached_thread, FALSE);
231
return ((GC_bool)result);
237
/* Thread table used if GC_win32_dll_threads is set. */
238
/* This is a fixed size array. */
239
/* Since we use runtime conditionals, both versions */
240
/* are always defined. */
242
# define MAX_THREADS 512
244
/* Things may get quite slow for large numbers of threads, */
245
/* since we look them up with sequential search. */
247
volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS];
249
volatile LONG GC_max_thread_index = 0;
250
/* Largest index in dll_thread_table */
251
/* that was ever used. */
253
/* And now the version used if GC_win32_dll_threads is not set. */
254
/* This is a chained hash table, with much of the code borrowed */
255
/* From the Posix implementation. */
256
# define THREAD_TABLE_SZ 256 /* Must be power of 2 */
257
GC_thread GC_threads[THREAD_TABLE_SZ];
260
/* Add a thread to GC_threads. We assume it wasn't already there. */
261
/* Caller holds allocation lock. */
262
/* Unlike the pthreads version, the id field is set by the caller. */
263
GC_thread GC_new_thread(DWORD id)
265
word hv = ((word)id) % THREAD_TABLE_SZ;
267
/* It may not be safe to allocate when we register the first thread. */
268
static struct GC_Thread_Rep first_thread;
269
static GC_bool first_thread_used = FALSE;
271
GC_ASSERT(I_HOLD_LOCK());
272
if (!first_thread_used) {
273
result = &first_thread;
274
first_thread_used = TRUE;
276
GC_ASSERT(!GC_win32_dll_threads);
277
result = (struct GC_Thread_Rep *)
278
GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
280
/* result can be NULL -> segfault */
281
GC_ASSERT(result -> flags == 0);
284
if (result == 0) return(0);
285
/* result -> id = id; Done by caller. */
286
result -> next = GC_threads[hv];
287
GC_threads[hv] = result;
289
GC_ASSERT(result -> flags == 0 /* && result -> thread_blocked == 0 */);
75
294
extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
296
#if defined(GWW_VDB) && defined(MPROTECT_VDB)
297
extern GC_bool GC_gww_dirty_init(void);
298
/* Defined in os_dep.c. Returns TRUE if GetWriteWatch is available. */
299
/* may be called repeatedly. */
302
GC_bool GC_in_thread_creation = FALSE; /* Protected by allocation lock. */
78
305
* This may be called from DllMain, and hence operates under unusual
306
* constraints. In particular, it must be lock-free if GC_win32_dll_threads
307
* is set. Always called from the thread being added.
308
* If GC_win32_dll_threads is not set, we already hold the allocation lock,
309
* except possibly during single-threaded start-up code.
81
static GC_thread GC_new_thread(void) {
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. */
311
static GC_thread GC_register_my_thread_inner(struct GC_stack_base *sb,
90
316
/* The following should be a noop according to the win32 */
91
317
/* documentation. There is empirical evidence that it */
93
319
# if defined(MPROTECT_VDB)
94
if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
320
# if defined(GWW_VDB)
321
if (GC_incremental && !GC_gww_dirty_init())
322
SetUnhandledExceptionFilter(GC_write_fault_handler);
324
if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
328
if (GC_win32_dll_threads) {
330
/* It appears to be unsafe to acquire a lock here, since this */
331
/* code is apparently not preeemptible on some systems. */
332
/* (This is based on complaints, not on Microsoft's official */
333
/* documentation, which says this should perform "only simple */
334
/* initialization tasks".) */
335
/* Hence we make do with nonblocking synchronization. */
336
/* It has been claimed that DllMain is really only executed with */
337
/* a particular system lock held, and thus careful use of locking */
338
/* around code that doesn't call back into the system libraries */
339
/* might be OK. But this hasn't been tested across all win32 */
96
341
/* 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 */
103
if (i == MAX_THREADS - 1)
104
ABORT("too many threads");
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);
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;
121
thread_table[i].pthread_id = pthread_self();
342
for (i = 0; InterlockedExchange((IE_t)&dll_thread_table[i].in_use,1) != 0;
344
/* Compare-and-swap would make this cleaner, but that's not */
345
/* supported before Windows 98 and NT 4.0. In Windows 2000, */
346
/* InterlockedExchange is supposed to be replaced by */
347
/* InterlockedExchangePointer, but that's not really what I */
349
/* FIXME: We should eventually declare Win95 dead and use AO_ */
350
/* primitives here. */
351
if (i == MAX_THREADS - 1)
352
ABORT("too many threads");
354
/* Update GC_max_thread_index if necessary. The following is safe, */
355
/* and unlike CompareExchange-based solutions seems to work on all */
356
/* Windows95 and later platforms. */
357
/* Unfortunately, GC_max_thread_index may be temporarily out of */
358
/* bounds, so readers have to compensate. */
359
while (i > GC_max_thread_index) {
360
InterlockedIncrement((IE_t)&GC_max_thread_index);
362
if (GC_max_thread_index >= MAX_THREADS) {
363
/* We overshot due to simultaneous increments. */
364
/* Setting it to MAX_THREADS-1 is always safe. */
365
GC_max_thread_index = MAX_THREADS - 1;
367
me = dll_thread_table + i;
368
} else /* Not using DllMain */ {
369
GC_ASSERT(I_HOLD_LOCK());
370
GC_in_thread_creation = TRUE; /* OK to collect from unknown thread. */
371
me = GC_new_thread(thread_id);
372
GC_in_thread_creation = FALSE;
375
/* me can be NULL -> segfault */
376
me -> pthread_id = pthread_self();
123
379
if (!DuplicateHandle(GetCurrentProcess(),
126
(HANDLE*)&thread_table[i].handle,
129
DUPLICATE_SAME_ACCESS)) {
382
(HANDLE*)&(me -> handle),
385
DUPLICATE_SAME_ACCESS)) {
130
386
DWORD last_error = GetLastError();
131
GC_printf1("Last error code: %lx\n", last_error);
387
GC_err_printf("Last error code: %d\n", last_error);
132
388
ABORT("DuplicateHandle failed");
134
thread_table[i].stack_base = GC_get_stack_base();
390
me -> stack_base = sb -> mem_base;
135
391
/* Up until this point, GC_push_all_stacks considers this thread */
137
if (thread_table[i].stack_base == NULL)
138
ABORT("Failed to find stack base in GC_new_thread");
139
393
/* Up until this point, this entry is viewed as reserved but invalid */
140
394
/* 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;
395
me -> id = thread_id;
396
# if defined(THREAD_LOCAL_ALLOC)
397
GC_init_thread_local((GC_tlfs)(&(me->tlfs)));
399
if (me -> stack_base == NULL)
400
ABORT("Bad stack base in GC_register_my_thread_inner");
401
if (GC_win32_dll_threads) {
402
if (GC_please_stop) {
403
AO_store(&GC_attached_thread, TRUE);
404
AO_nop_full(); // Later updates must become visible after this.
406
/* We'd like to wait here, but can't, since waiting in DllMain */
407
/* provokes deadlocks. */
408
/* Thus we force marking to be restarted instead. */
410
GC_ASSERT(!GC_please_stop);
411
/* Otherwise both we and the thread stopping code would be */
412
/* holding the allocation lock. */
414
return (GC_thread)(me);
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)
169
CloseHandle(thr->handle);
170
/* cast away volatile qualifier */
432
/* Return the GC_thread corresponding to a thread id. May be called */
433
/* without a lock, but should be called in contexts in which the */
434
/* requested thread cannot be asynchronously deleted, e.g. from the */
436
/* This version assumes that either GC_win32_dll_threads is set, or */
437
/* we hold the allocator lock. */
438
/* Also used (for assertion checking only) from thread_local_alloc.c. */
439
GC_thread GC_lookup_thread_inner(DWORD thread_id) {
440
if (GC_win32_dll_threads) {
442
LONG my_max = GC_get_max_thread_index();
445
(!AO_load_acquire(&(dll_thread_table[i].in_use))
446
|| dll_thread_table[i].id != thread_id);
447
/* Must still be in_use, since nobody else can store our thread_id. */
452
return (GC_thread)(dll_thread_table + i);
455
word hv = ((word)thread_id) % THREAD_TABLE_SZ;
456
register GC_thread p = GC_threads[hv];
458
GC_ASSERT(I_HOLD_LOCK());
459
while (p != 0 && p -> id != thread_id) p = p -> next;
464
/* A version of the above that acquires the lock if necessary. Note */
465
/* that the identically named function for pthreads is different, and */
466
/* just assumes we hold the lock. */
467
/* Also used (for assertion checking only) from thread_local_alloc.c. */
468
static GC_thread GC_lookup_thread(DWORD thread_id)
470
if (GC_win32_dll_threads) {
471
return GC_lookup_thread_inner(thread_id);
475
result = GC_lookup_thread_inner(thread_id);
481
/* If a thread has been joined, but we have not yet */
482
/* been notified, then there may be more than one thread */
483
/* in the table with the same win32 id. */
484
/* This is OK, but we need a way to delete a specific one. */
485
/* Assumes we hold the allocation lock unless */
486
/* GC_win32_dll_threads is set. */
487
/* If GC_win32_dll_threads is set it should be called from the */
488
/* thread being deleted. */
489
void GC_delete_gc_thread(GC_vthread gc_id)
491
CloseHandle(gc_id->handle);
492
if (GC_win32_dll_threads) {
493
/* This is intended to be lock-free. */
494
/* It is either called synchronously from the thread being deleted, */
495
/* or by the joining thread. */
496
/* In this branch asynchronosu changes to *gc_id are possible. */
497
gc_id -> stack_base = 0;
500
gc_id -> pthread_id = 0;
175
501
# endif /* CYGWIN32 */
179
static void GC_delete_thread(DWORD thread_id) {
181
LONG my_max = GC_get_max_thread_index();
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. */
189
WARN("Removing nonexistent thread %ld\n", (GC_word)thread_id);
191
GC_delete_gc_thread(thread_table+i);
502
# ifdef GC_WIN32_PTHREADS
503
gc_id -> pthread_id.p = NULL;
504
# endif /* GC_WIN32_PTHREADS */
505
AO_store_release(&(gc_id->in_use), FALSE);
507
/* Cast away volatile qualifier, since we have lock. */
508
GC_thread gc_nvid = (GC_thread)gc_id;
509
DWORD id = gc_nvid -> id;
510
word hv = ((word)id) % THREAD_TABLE_SZ;
511
register GC_thread p = GC_threads[hv];
512
register GC_thread prev = 0;
514
GC_ASSERT(I_HOLD_LOCK());
515
while (p != gc_nvid) {
520
GC_threads[hv] = p -> next;
522
prev -> next = p -> next;
528
/* Delete a thread from GC_threads. We assume it is there. */
529
/* (The code intentionally traps if it wasn't.) */
530
/* Assumes we hold the allocation lock unless */
531
/* GC_win32_dll_threads is set. */
532
/* If GC_win32_dll_threads is set it should be called from the */
533
/* thread being deleted. */
534
void GC_delete_thread(DWORD id)
536
if (GC_win32_dll_threads) {
537
GC_thread t = GC_lookup_thread_inner(id);
540
WARN("Removing nonexistent thread %ld\n", (GC_word)id);
542
GC_delete_gc_thread(t);
545
word hv = ((word)id) % THREAD_TABLE_SZ;
546
register GC_thread p = GC_threads[hv];
547
register GC_thread prev = 0;
549
GC_ASSERT(I_HOLD_LOCK());
550
while (p -> id != id) {
554
CloseHandle(p->handle);
556
GC_threads[hv] = p -> next;
558
prev -> next = p -> next;
564
GC_API int GC_register_my_thread(struct GC_stack_base *sb) {
565
DWORD t = GetCurrentThreadId();
567
if (0 == GC_lookup_thread(t)) {
568
/* We lock here, since we want to wait for an ongoing GC. */
570
GC_register_my_thread_inner(sb, t);
578
GC_API int GC_unregister_my_thread(void)
580
DWORD t = GetCurrentThreadId();
582
# if defined(THREAD_LOCAL_ALLOC)
585
GC_thread me = GC_lookup_thread_inner(t);
586
GC_destroy_thread_local(&(me->tlfs));
590
if (GC_win32_dll_threads) {
591
/* Should we just ignore this? */
604
/* A quick-and-dirty cache of the mapping between pthread_t */
605
/* and win32 thread id. */
606
#define PTHREAD_MAP_SIZE 512
607
DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE];
608
#define HASH(pthread_id) ((NUMERIC_THREAD_ID(pthread_id) >> 5) % PTHREAD_MAP_SIZE)
609
/* It appears pthread_t is really a pointer type ... */
610
#define SET_PTHREAD_MAP_CACHE(pthread_id, win32_id) \
611
GC_pthread_map_cache[HASH(pthread_id)] = (win32_id);
612
#define GET_PTHREAD_MAP_CACHE(pthread_id) \
613
GC_pthread_map_cache[HASH(pthread_id)]
198
615
/* Return a GC_thread corresponding to a given pthread_t. */
199
616
/* Returns 0 if it's not there. */
200
617
/* 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)
618
/* have not yet terminated or are still joinable, and */
619
/* cannot be concurrently terminated. */
620
/* Assumes we do NOT hold the allocation lock. */
621
static GC_thread GC_lookup_pthread(pthread_t id)
205
LONG my_max = GC_get_max_thread_index();
623
if (GC_win32_dll_threads) {
625
LONG my_max = GC_get_max_thread_index();
209
(!thread_table[i].in_use || thread_table[i].pthread_id != id
210
|| !thread_table[i].in_use);
629
(!AO_load_acquire(&(dll_thread_table[i].in_use))
630
|| THREAD_EQUAL(dll_thread_table[i].pthread_id, id));
211
631
/* Must still be in_use, since nobody else can store our thread_id. */
213
if (i > my_max) return 0;
214
return thread_table + i;
633
if (i > my_max) return 0;
634
return (GC_thread)(dll_thread_table + i);
636
/* We first try the cache. If that fails, we use a very slow */
638
int hv_guess = GET_PTHREAD_MAP_CACHE(id) % THREAD_TABLE_SZ;
643
for (p = GC_threads[hv_guess]; 0 != p; p = p -> next) {
644
if (THREAD_EQUAL(p -> pthread_id, id))
647
for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
648
for (p = GC_threads[hv]; 0 != p; p = p -> next) {
649
if (THREAD_EQUAL(p -> pthread_id, id))
217
#endif /* CYGWIN32 */
660
#endif /* GC_PTHREADS */
219
void GC_push_thread_structures GC_PROTO((void))
662
void GC_push_thread_structures(void)
664
GC_ASSERT(I_HOLD_LOCK());
665
if (GC_win32_dll_threads) {
221
666
/* Unlike the other threads implementations, the thread table here */
222
667
/* contains no pointers to the collectable heap. Thus we have */
223
668
/* no private structures we need to preserve. */
225
{ int i; /* pthreads may keep a pointer in the thread exit value */
226
LONG my_max = GC_get_max_thread_index();
670
{ int i; /* pthreads may keep a pointer in the thread exit value */
671
LONG my_max = GC_get_max_thread_index();
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));
673
for (i = 0; i <= my_max; i++)
674
if (dll_thread_table[i].in_use)
675
GC_push_all((ptr_t)&(dll_thread_table[i].status),
676
(ptr_t)(&(dll_thread_table[i].status)+1));
680
GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
682
# if defined(THREAD_LOCAL_ALLOC)
683
GC_push_all((ptr_t)(&GC_thread_key),
684
(ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key));
685
/* Just in case we ever use our own TLS implementation. */
689
/* Suspend the given thread, if it's still active. */
690
void GC_suspend(GC_thread t)
693
/* SuspendThread will fail if thread is running kernel code */
694
while (SuspendThread(t -> handle) == (DWORD)-1)
697
/* Apparently the Windows 95 GetOpenFileName call creates */
698
/* a thread that does not properly get cleaned up, and */
699
/* SuspendThread on its descriptor may provoke a crash. */
700
/* This reduces the probability of that event, though it still */
701
/* appears there's a race here. */
703
if (GetExitCodeThread(t -> handle, &exitCode) &&
704
exitCode != STILL_ACTIVE) {
705
t -> stack_base = 0; /* prevent stack from being pushed */
707
/* this breaks pthread_join on Cygwin, which is guaranteed to */
708
/* only see user pthreads */
709
GC_ASSERT(GC_win32_dll_threads);
710
GC_delete_gc_thread(t);
714
if (SuspendThread(t -> handle) == (DWORD)-1)
715
ABORT("SuspendThread failed");
717
t -> suspended = TRUE;
236
720
/* Defined in misc.c */
237
extern CRITICAL_SECTION GC_write_cs;
722
extern CRITICAL_SECTION GC_write_cs;
725
void GC_stop_world(void)
241
727
DWORD thread_id = GetCurrentThreadId();
244
730
if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
731
GC_ASSERT(I_HOLD_LOCK());
246
733
GC_please_stop = TRUE;
247
734
# ifndef CYGWIN32
248
735
EnterCriticalSection(&GC_write_cs);
249
# endif /* !CYGWIN32 */
250
for (i = 0; i <= GC_get_max_thread_index(); i++)
251
if (thread_table[i].stack_base != 0
252
&& thread_table[i].id != thread_id) {
254
/* SuspendThread will fail if thread is running kernel code */
255
while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
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. */
264
if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
265
exitCode != STILL_ACTIVE) {
266
thread_table[i].stack_base = 0; /* prevent stack from being pushed */
268
/* this breaks pthread_join on Cygwin, which is guaranteed to */
269
/* only see user pthreads */
270
thread_table[i].in_use = FALSE;
271
CloseHandle(thread_table[i].handle);
737
if (GC_win32_dll_threads) {
738
/* Any threads being created during this loop will end up setting */
739
/* GC_attached_thread when they start. This will force marking to */
741
/* This is not ideal, but hopefully correct. */
742
GC_attached_thread = FALSE;
743
for (i = 0; i <= GC_get_max_thread_index(); i++) {
744
GC_vthread t = dll_thread_table + i;
745
if (t -> stack_base != 0
746
&& t -> id != thread_id) {
747
GC_suspend((GC_thread)t);
754
for (i = 0; i < THREAD_TABLE_SZ; i++) {
755
for (t = GC_threads[i]; t != 0; t = t -> next) {
756
if (t -> stack_base != 0
757
&& !KNOWN_FINISHED(t)
758
&& t -> id != thread_id) {
275
if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
276
ABORT("SuspendThread failed");
278
thread_table[i].suspended = TRUE;
280
764
# ifndef CYGWIN32
281
765
LeaveCriticalSection(&GC_write_cs);
282
# endif /* !CYGWIN32 */
285
void GC_start_world()
769
void GC_start_world(void)
287
771
DWORD thread_id = GetCurrentThreadId();
289
773
LONG my_max = GC_get_max_thread_index();
291
for (i = 0; i <= my_max; i++)
292
if (thread_table[i].stack_base != 0 && thread_table[i].suspended
293
&& thread_table[i].id != thread_id) {
294
if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
295
ABORT("ResumeThread failed");
296
thread_table[i].suspended = FALSE;
775
GC_ASSERT(I_HOLD_LOCK());
776
if (GC_win32_dll_threads) {
777
for (i = 0; i <= my_max; i++) {
778
GC_thread t = (GC_thread)(dll_thread_table + i);
779
if (t -> stack_base != 0 && t -> suspended
780
&& t -> id != thread_id) {
781
if (ResumeThread(t -> handle) == (DWORD)-1)
782
ABORT("ResumeThread failed");
783
t -> suspended = FALSE;
790
for (i = 0; i < THREAD_TABLE_SZ; i++) {
791
for (t = GC_threads[i]; t != 0; t = t -> next) {
792
if (t -> stack_base != 0 && t -> suspended
793
&& t -> id != thread_id) {
794
if (ResumeThread(t -> handle) == (DWORD)-1)
795
ABORT("ResumeThread failed");
796
t -> suspended = FALSE;
298
801
GC_please_stop = FALSE;
302
# pragma warning(disable:4715)
304
ptr_t GC_current_stackbottom()
306
DWORD thread_id = GetCurrentThreadId();
308
LONG my_max = GC_get_max_thread_index();
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;
313
ABORT("no thread table entry for current thread");
316
# pragma warning(default:4715)
320
805
/* The VirtualQuery calls below won't work properly on WinCE, but */
321
806
/* since each stack is restricted to an aligned 64K region of */
506
1006
#ifndef __GNUC__
508
1008
#endif /* __GNUC__ */
509
ret = args->start (args->param);
1009
ret = (void *)(size_t)args->start (args->param);
510
1010
#ifndef __GNUC__
512
1012
#endif /* __GNUC__ */
1013
GC_unregister_my_thread();
514
GC_delete_thread(GetCurrentThreadId());
515
1015
#ifndef __GNUC__
517
1017
#endif /* __GNUC__ */
1019
# if DEBUG_WIN32_THREADS
1020
GC_printf("thread 0x%x returned from start routine.\n",
1021
GetCurrentThreadId());
521
#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
523
#endif /* !CYGWIN32 */
1026
DWORD WINAPI GC_win32_start(LPVOID arg)
1028
return (DWORD)(size_t)GC_call_with_stack_base(GC_win32_start_inner, arg);
1031
GC_API HANDLE WINAPI GC_CreateThread(
1032
LPSECURITY_ATTRIBUTES lpThreadAttributes,
1033
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
1034
LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
1036
HANDLE thread_h = NULL;
1040
if (!parallel_initialized) GC_init_parallel();
1041
/* make sure GC is initialized (i.e. main thread is attached,
1044
# if DEBUG_WIN32_THREADS
1045
GC_printf("About to create a thread from 0x%x\n", GetCurrentThreadId());
1047
if (GC_win32_dll_threads) {
1048
return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
1049
lpParameter, dwCreationFlags, lpThreadId);
1051
args = GC_malloc_uncollectable(sizeof(thread_args));
1052
/* Handed off to and deallocated by child thread. */
1054
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1058
/* set up thread arguments */
1059
args -> start = lpStartAddress;
1060
args -> param = lpParameter;
1062
GC_need_to_lock = TRUE;
1063
thread_h = CreateThread(lpThreadAttributes,
1064
dwStackSize, GC_win32_start,
1065
args, dwCreationFlags,
1067
if( thread_h == 0 ) GC_free( args );
1072
void WINAPI GC_ExitThread(DWORD dwExitCode)
1074
GC_unregister_my_thread();
1075
ExitThread(dwExitCode);
1078
uintptr_t GC_beginthreadex(
1079
void *security, unsigned stack_size,
1080
unsigned ( __stdcall *start_address )( void * ),
1081
void *arglist, unsigned initflag, unsigned *thrdaddr)
1087
if (!parallel_initialized) GC_init_parallel();
1088
/* make sure GC is initialized (i.e. main thread is attached,
1090
# if DEBUG_WIN32_THREADS
1091
GC_printf("About to create a thread from 0x%x\n", GetCurrentThreadId());
1094
if (GC_win32_dll_threads) {
1095
return _beginthreadex(security, stack_size, start_address,
1096
arglist, initflag, thrdaddr);
1098
args = GC_malloc_uncollectable(sizeof(thread_args));
1099
/* Handed off to and deallocated by child thread. */
1101
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1102
return (uintptr_t)(-1L);
1105
/* set up thread arguments */
1106
args -> start = (LPTHREAD_START_ROUTINE)start_address;
1107
args -> param = arglist;
1109
GC_need_to_lock = TRUE;
1110
thread_h = _beginthreadex(security, stack_size,
1111
(unsigned (__stdcall *) (void *))GC_win32_start,
1112
args, initflag, thrdaddr);
1113
if( thread_h == 0 ) GC_free( args );
1118
void GC_endthreadex(unsigned retval)
1120
GC_unregister_my_thread();
1121
_endthreadex(retval);
1124
#endif /* !GC_PTHREADS */
800
1484
#endif /* GC_DLL */
801
#endif /* !CYGWIN32 */
1485
#endif /* !GC_PTHREADS */
803
1487
# endif /* !MSWINCE */
1489
/* Perform all initializations, including those that */
1490
/* may require allocation. */
1491
/* Called without allocation lock. */
1492
/* Must be called before a second thread is created. */
1493
void GC_init_parallel(void)
1495
if (parallel_initialized) return;
1496
parallel_initialized = TRUE;
1497
/* GC_init() calls us back, so set flag first. */
1499
if (!GC_is_initialized) GC_init();
1500
if (GC_win32_dll_threads) {
1501
GC_need_to_lock = TRUE;
1502
/* Cannot intercept thread creation. Hence we don't know if */
1503
/* other threads exist. However, client is not allowed to */
1504
/* create other threads before collector initialization. */
1505
/* Thus it's OK not to lock before this. */
1507
/* Initialize thread local free lists if used. */
1508
# if defined(THREAD_LOCAL_ALLOC)
1510
GC_init_thread_local(&(GC_lookup_thread(GetCurrentThreadId())->tlfs));
1515
#if defined(USE_PTHREAD_LOCKS)
1516
/* Support for pthread locking code. */
1517
/* Pthread_mutex_try_lock may not win here, */
1518
/* due to builtinsupport for spinning first? */
1520
volatile GC_bool GC_collecting = 0;
1521
/* A hint that we're in the collector and */
1522
/* holding the allocation lock for an */
1523
/* extended period. */
1527
pthread_mutex_lock(&GC_allocate_ml);
1529
#endif /* USE_PTHREAD ... */
1531
# if defined(THREAD_LOCAL_ALLOC)
1533
/* Add thread-local allocation support. Microsoft uses __declspec(thread) */
1535
/* We must explicitly mark ptrfree and gcj free lists, since the free */
1536
/* list links wouldn't otherwise be found. We also set them in the */
1537
/* normal free lists, since that involves touching less memory than if */
1538
/* we scanned them normally. */
1539
void GC_mark_thread_local_free_lists(void)
1544
for (i = 0; i < THREAD_TABLE_SZ; ++i) {
1545
for (p = GC_threads[i]; 0 != p; p = p -> next) {
1546
GC_mark_thread_local_fls_for(&(p->tlfs));
1551
#if defined(GC_ASSERTIONS)
1552
/* Check that all thread-local free-lists are completely marked. */
1553
/* also check that thread-specific-data structures are marked. */
1554
void GC_check_tls(void) {
1558
for (i = 0; i < THREAD_TABLE_SZ; ++i) {
1559
for (p = GC_threads[i]; 0 != p; p = p -> next) {
1560
GC_check_tls_for(&(p->tlfs));
1563
# if defined(USE_CUSTOM_SPECIFIC)
1564
if (GC_thread_key != 0)
1565
GC_check_tsd_marks(GC_thread_key);
1568
#endif /* GC_ASSERTIONS */
1570
#endif /* THREAD_LOCAL_ALLOC ... */
805
1572
#endif /* GC_WIN32_THREADS */