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	*/
GC_use_DllMain(void)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
GC_use_DllMain(void)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.		*/
GC_started_thread_while_stopped(void)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.	*/
GC_new_thread(DWORD id)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  */
GC_register_my_thread_inner(struct GC_stack_base * sb,DWORD thread_id)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
GC_get_max_thread_index()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.	*/
GC_lookup_thread_inner(DWORD thread_id)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.	*/
GC_lookup_thread(DWORD thread_id)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.					*/
GC_delete_gc_thread(GC_vthread gc_id)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.					*/
GC_delete_thread(DWORD id)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 
GC_register_my_thread(struct GC_stack_base * sb)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 
GC_unregister_my_thread(void)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.			*/
GC_lookup_pthread(pthread_t id)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 
GC_push_thread_structures(void)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.	*/
GC_suspend(GC_thread t)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 
GC_stop_world(void)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 
GC_start_world(void)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
GC_get_stack_min(ptr_t s)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 
GC_push_stack_for(GC_thread thread)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 
GC_push_all_stacks(void)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 
GC_get_next_stack(char * start,char ** lo,char ** hi)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 
GC_win32_start_inner(struct GC_stack_base * sb,LPVOID arg)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 
GC_win32_start(LPVOID arg)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 
GC_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId)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 
GC_ExitThread(DWORD dwExitCode)1070 void WINAPI GC_ExitThread(DWORD dwExitCode)
1071 {
1072   GC_unregister_my_thread();
1073   ExitThread(dwExitCode);
1074 }
1075 
GC_beginthreadex(void * security,unsigned stack_size,unsigned (__stdcall * start_address)(void *),void * arglist,unsigned initflag,unsigned * thrdaddr)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 
GC_endthreadex(unsigned retval)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 
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nShowCmd)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 
main_thread_start(LPVOID arg)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.	*/
GC_thr_init(void)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 
GC_pthread_join(pthread_t pthread_id,void ** retval)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
GC_pthread_create(pthread_t * new_thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)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 
GC_pthread_start_inner(struct GC_stack_base * sb,void * arg)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 
GC_pthread_start(void * arg)1354 void * GC_pthread_start(void * arg)
1355 {
1356     return GC_call_with_stack_base(GC_pthread_start_inner, arg);
1357 }
1358 
GC_thread_exit_proc(void * arg)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... */
GC_pthread_sigmask(int how,const sigset_t * set,sigset_t * oset)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 
GC_pthread_detach(pthread_t thread)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
DllMain(HINSTANCE inst,ULONG reason,LPVOID reserved)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.	*/
GC_init_parallel(void)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 
GC_lock(void)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.						*/
GC_mark_thread_local_free_lists(void)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.	*/
GC_check_tls(void)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 */
1571