#endif /* !DARWIN_DONT_PARSE_STACK */
-#define DARWIN_QUERY_TASK_THREADS 1 /* FIXME: Remove this. */
-
/* GC_query_task_threads controls whether to obtain the list of */
/* the threads from the kernel or to use GC_threads table. */
-#ifdef DARWIN_SUSPEND_GC_THREADS
+#ifdef GC_NO_THREADS_DISCOVERY
# define GC_query_task_threads FALSE
-#elif defined(DARWIN_QUERY_TASK_THREADS)
+#elif defined(GC_DISCOVER_TASK_THREADS)
# define GC_query_task_threads TRUE
#else
STATIC GC_bool GC_query_task_threads = FALSE;
-#endif /* !DARWIN_SUSPEND_GC_THREADS */
+#endif /* !GC_NO_THREADS_DISCOVERY */
-/* FIXME: add GC_API and declare in gc.h; add comment; document macros */
-void GC_CALL GC_use_threads_discovery(void)
+/* Use implicit threads registration (all task threads excluding the GC */
+/* special ones are stoped and scanned). Should be called before */
+/* GC_INIT() (or, at least, before going multi-threaded). Deprecated. */
+GC_API void GC_CALL GC_use_threads_discovery(void)
{
-# if defined(DARWIN_SUSPEND_GC_THREADS) || defined(DARWIN_DONT_PARSE_STACK)
+# if defined(GC_NO_THREADS_DISCOVERY) || defined(DARWIN_DONT_PARSE_STACK)
ABORT("Darwin task-threads-based stop and push unsupported");
# else
GC_ASSERT(!GC_need_to_lock);
-# ifndef DARWIN_QUERY_TASK_THREADS
+# ifndef GC_DISCOVER_TASK_THREADS
GC_query_task_threads = TRUE;
# endif
GC_init_parallel(); /* just to be consistent with Win32 one */
GC_total_stacksize = total_size;
}
-#ifndef DARWIN_SUSPEND_GC_THREADS
+#ifndef GC_NO_THREADS_DISCOVERY
STATIC mach_port_t GC_mach_handler_thread = 0;
STATIC GC_bool GC_use_mach_handler_thread = FALSE;
return changed;
}
-#endif /* !DARWIN_SUSPEND_GC_THREADS */
+#endif /* !GC_NO_THREADS_DISCOVERY */
#ifdef MPROTECT_VDB
GC_INNER void GC_mprotect_stop(void);
# endif /* PARALLEL_MARK */
if (GC_query_task_threads) {
-# ifndef DARWIN_SUSPEND_GC_THREADS
+# ifndef GC_NO_THREADS_DISCOVERY
GC_bool changed;
thread_act_array_t act_list, prev_list;
mach_msg_type_number_t listcount, prevcount;
mach_port_deallocate(my_task, prev_list[i]);
vm_deallocate(my_task, (vm_address_t)act_list,
sizeof(thread_t) * listcount);
-# endif /* !DARWIN_SUSPEND_GC_THREADS */
+# endif /* !GC_NO_THREADS_DISCOVERY */
} else {
for (i = 0; i < THREAD_TABLE_SZ; i++) {
# endif
if (GC_query_task_threads) {
-# ifndef DARWIN_SUSPEND_GC_THREADS
+# ifndef GC_NO_THREADS_DISCOVERY
int j = GC_mach_threads_count;
kern_return_t kern_result;
thread_act_array_t act_list;
}
vm_deallocate(my_task, (vm_address_t)act_list,
sizeof(thread_t) * listcount);
-# endif /* !DARWIN_SUSPEND_GC_THREADS */
+# endif /* !GC_NO_THREADS_DISCOVERY */
} else {
mach_port_t my_thread = mach_thread_self();
/* DllMain-based thread registration is currently incompatible */
/* with thread-local allocation, pthreads and WinCE. */
-#if defined(GC_DLL) && !defined(GC_NO_DLLMAIN) && !defined(MSWINCE) \
+#if defined(GC_DLL) && !defined(GC_NO_THREADS_DISCOVERY) && !defined(MSWINCE) \
&& !defined(THREAD_LOCAL_ALLOC) && !defined(GC_PTHREADS)
# include "atomic_ops.h"
- STATIC GC_bool GC_win32_dll_threads = FALSE;
/* This code operates in two distinct modes, depending on */
- /* the setting of GC_win32_dll_threads. If */
- /* GC_win32_dll_threads is set, all threads in the process */
+ /* the setting of GC_win32_dll_threads. */
+ /* If GC_win32_dll_threads is set, all threads in the process */
/* are implicitly registered with the GC by DllMain. */
/* No explicit registration is required, and attempts at */
/* explicit registration are ignored. This mode is */
/* In this mode access to the thread table is lock-free. */
/* Hence there is a static limit on the number of threads. */
- /* If GC_win32_dll_threads is FALSE, or the collector is */
- /* built without GC_DLL defined, things operate in a way */
+# ifdef GC_DISCOVER_TASK_THREADS
+ /* GC_DISCOVER_TASK_THREADS should be used if DllMain-based */
+ /* thread registration is required but it is impossible to */
+ /* call GC_use_threads_discovery before other GC routines. */
+# define GC_win32_dll_threads TRUE
+# else
+ STATIC GC_bool GC_win32_dll_threads = FALSE;
+ /* GC_win32_dll_threads must be set (if needed) at the */
+ /* application initialization time, i.e. before any */
+ /* collector or thread calls. We make it a "dynamic" */
+ /* option only to avoid multiple library versions. */
+# endif
+
+#else
+ /* If GC_win32_dll_threads is FALSE (or the collector is */
+ /* built without GC_DLL defined), things operate in a way */
/* that is very similar to Posix platforms, and new threads */
/* must be registered with the collector, e.g. by using */
/* preprocessor-based interception of the thread primitives. */
/* the basic collector rely on such facilities, but an */
/* optional package that intercepts thread calls this way */
/* would probably be nice. */
-
- /* GC_win32_dll_threads must be set at initialization time, */
- /* i.e. before any collector or thread calls. We make it a */
- /* "dynamic" option only to avoid multiple library versions. */
-#else
-# ifndef GC_NO_DLLMAIN
-# define GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
+# define GC_NO_THREADS_DISCOVERY
# endif
# define GC_win32_dll_threads FALSE
# undef MAX_THREADS
# define MAX_THREADS 1 /* dll_thread_table[] is always empty. */
-#endif
+#endif /* GC_NO_THREADS_DISCOVERY */
/* We have two versions of the thread table. Which one */
/* we us depends on whether or not GC_win32_dll_threads */
static GC_bool parallel_initialized = FALSE;
-/* GC_use_DllMain() is currently incompatible with pthreads and WinCE. */
-/* It might be possible to get DllMain-based thread registration to */
-/* work with Cygwin, but if you try, you are on your own. */
-GC_API void GC_CALL GC_use_DllMain(void)
+/* GC_use_threads_discovery() is currently incompatible with pthreads */
+/* and WinCE. It might be possible to get DllMain-based thread */
+/* registration to work with Cygwin, but if you try it then you are on */
+/* your own. */
+GC_API void GC_CALL GC_use_threads_discovery(void)
{
-# ifdef GC_NO_DLLMAIN
+# ifdef GC_NO_THREADS_DISCOVERY
ABORT("GC DllMain-based thread registration unsupported");
# else
/* Turn on GC_win32_dll_threads. */
GC_ASSERT(!parallel_initialized);
- GC_win32_dll_threads = TRUE;
+# ifndef GC_DISCOVER_TASK_THREADS
+ GC_win32_dll_threads = TRUE;
+# endif
GC_init_parallel();
# endif
}
struct GC_Thread_Rep {
union {
-# ifndef GC_NO_DLLMAIN
- AO_t in_use; /* Updated without lock. */
+# ifndef GC_NO_THREADS_DISCOVERY
+ AO_t in_use; /* Updated without lock. */
/* We assert that unused */
/* entries have invalid ids of */
/* zero and zero stack fields. */
typedef struct GC_Thread_Rep * GC_thread;
typedef volatile struct GC_Thread_Rep * GC_vthread;
-#ifndef GC_NO_DLLMAIN
+#ifndef GC_NO_THREADS_DISCOVERY
/* We assumed that volatile ==> memory ordering, at least among */
/* volatiles. This code should consistently use atomic_ops. */
STATIC volatile GC_bool GC_please_stop = FALSE;
* If we notice this in the middle of marking.
*/
-#ifndef GC_NO_DLLMAIN
+#ifndef GC_NO_THREADS_DISCOVERY
STATIC AO_t GC_attached_thread = FALSE;
#endif
/* since GC_attached_thread was explicitly reset. */
GC_bool GC_started_thread_while_stopped(void)
{
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
AO_t result;
if (GC_win32_dll_threads) {
result = AO_load(&GC_attached_thread);
if (result) {
AO_store(&GC_attached_thread, FALSE);
+ return TRUE;
}
- return ((GC_bool)result);
}
# endif
return FALSE;
STATIC GC_bool GC_in_thread_creation = FALSE;
/* Protected by allocation lock. */
-/*
- * This may be called from DllMain, and hence operates under unusual
- * constraints. In particular, it must be lock-free if GC_win32_dll_threads
- * is set. Always called from the thread being added.
- * If GC_win32_dll_threads is not set, we already hold the allocation lock,
- * except possibly during single-threaded start-up code.
- */
+/* This may be called from DllMain, and hence operates under unusual */
+/* constraints. In particular, it must be lock-free if */
+/* GC_win32_dll_threads is set. Always called from the thread being */
+/* added. If GC_win32_dll_threads is not set, we already hold the */
+/* allocation lock except possibly during single-threaded startup code. */
STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb,
DWORD thread_id)
{
# endif
# endif
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
int i;
/* It appears to be unsafe to acquire a lock here, since this */
# endif
if (me -> stack_base == NULL)
ABORT("Bad stack base in GC_register_my_thread_inner");
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
if (GC_please_stop) {
AO_store(&GC_attached_thread, TRUE);
/* Also used (for assertion checking only) from thread_local_alloc.c. */
STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id)
{
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
int i;
LONG my_max = GC_get_max_thread_index();
i++) {
/* empty */
}
- if (i > my_max) {
- return 0;
- } else {
- return (GC_thread)(dll_thread_table + i);
- }
- }
+ return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL;
+ } else
# endif
- {
+ /* else */ {
word hv = THREAD_TABLE_INDEX(thread_id);
register GC_thread p = GC_threads[hv];
/* lock may be required for fault handling. */
#if defined(MPROTECT_VDB)
# define UNPROTECT_THREAD(t) \
- if (GC_dirty_maintained && !GC_win32_dll_threads && \
- t != &first_thread) { \
+ if (!GC_win32_dll_threads && GC_dirty_maintained \
+ && t != &first_thread) { \
GC_ASSERT(SMALL_OBJ(GC_size(t))); \
GC_remove_protection(HBLKPTR(t), 1, FALSE); \
}
# ifndef MSWINCE
CloseHandle(gc_id->handle);
# endif
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
/* This is intended to be lock-free. */
/* It is either called synchronously from the thread being */
/* Check GC is initialized and the current thread is registered. */
GC_ASSERT(GC_lookup_thread_inner(GetCurrentThreadId()) != 0);
-# if !defined(GC_NO_DLLMAIN) && !defined(PARALLEL_MARK)
+# if !defined(GC_NO_THREADS_DISCOVERY) && !defined(PARALLEL_MARK)
/* GC_init() doesn't call GC_init_parallel() in this case. */
parallel_initialized = TRUE;
# endif
GC_API int GC_CALL GC_unregister_my_thread(void)
{
- DWORD t = GetCurrentThreadId();
DCL_LOCK_STATE;
/* FIXME: is GC_wait_for_gc_completion(FALSE) needed here? */
if (GC_win32_dll_threads) {
# if defined(THREAD_LOCAL_ALLOC)
- /* Can't happen: see GC_use_DllMain(). */
+ /* Can't happen: see GC_use_threads_discovery(). */
GC_ASSERT(FALSE);
+# else
+ /* FIXME: Should we just ignore this? */
+ GC_delete_thread(GetCurrentThreadId());
# endif
- /* FIXME: Should we just ignore this? */
- GC_delete_thread(t);
} else {
+ DWORD t = GetCurrentThreadId();
LOCK();
# if defined(THREAD_LOCAL_ALLOC)
{
/* Assumes we do NOT hold the allocation lock. */
STATIC GC_thread GC_lookup_pthread(pthread_t id)
{
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
int i;
LONG my_max = GC_get_max_thread_index();
i++) {
/* empty */
}
- if (i > my_max) return 0;
- return (GC_thread)(dll_thread_table + i);
- }
+ return i <= my_max ? (GC_thread)(dll_thread_table + i) : NULL;
+ } else
# endif
- {
+ /* else */ {
/* We first try the cache. If that fails, we use a very slow */
/* approach. */
word hv_guess = THREAD_TABLE_INDEX(GET_PTHREAD_MAP_CACHE(id));
void GC_push_thread_structures(void)
{
GC_ASSERT(I_HOLD_LOCK());
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
/* Unlike the other threads implementations, the thread table here */
/* contains no pointers to the collectable heap. Thus we have */
}
# endif /* PARALLEL_MARK */
-# if !defined(GC_NO_DLLMAIN) || defined(GC_ASSERTIONS)
+# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS)
GC_please_stop = TRUE;
# endif
# ifndef CYGWIN32
GC_write_disabled = TRUE;
# endif
# endif
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
int i;
int my_max;
}
}
}
-# if !defined(GC_NO_DLLMAIN) || defined(GC_ASSERTIONS)
+# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS)
GC_please_stop = FALSE;
# endif
}
unsigned nthreads = 0;
# endif
word total_size = 0;
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
if (GC_win32_dll_threads) {
int i;
LONG my_max = GC_get_max_thread_index();
HMODULE hK32;
/* SignalObjectAndWait() API call works only under NT. */
# endif
- if (GC_markers <= 1 || GC_win32_dll_threads
+ if (GC_win32_dll_threads || GC_markers <= 1
# if !defined(GC_PTHREADS_PARAMARK) && !defined(MSWINCE) \
&& !defined(DONT_USE_SIGNALANDWAIT)
|| GC_wnt == FALSE
LOCK();
GC_delete_gc_thread(joinee);
UNLOCK();
- } /* otherwise dllmain handles it. */
+ } /* otherwise DllMain handles it. */
# if DEBUG_CYGWIN_THREADS
GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
- int result;
- struct start_info * si;
-
if (!parallel_initialized) GC_init_parallel();
/* make sure GC is initialized (i.e. main thread is attached) */
if (GC_win32_dll_threads) {
return pthread_create(new_thread, attr, start_routine, arg);
- }
-
- /* This is otherwise saved only in an area mmapped by the thread */
- /* library, which isn't visible to the collector. */
- si = GC_malloc_uncollectable(sizeof(struct start_info));
- if (0 == si) return(EAGAIN);
-
- si -> start_routine = start_routine;
- si -> arg = arg;
- if (attr != 0 &&
- pthread_attr_getdetachstate(attr, &si->detached)
- == PTHREAD_CREATE_DETACHED) {
- si->detached = TRUE;
- }
+ } else {
+ int result;
+ struct start_info * si;
+
+ /* This is otherwise saved only in an area mmapped by the thread */
+ /* library, which isn't visible to the collector. */
+ si = GC_malloc_uncollectable(sizeof(struct start_info));
+ if (0 == si) return(EAGAIN);
+
+ si -> start_routine = start_routine;
+ si -> arg = arg;
+ if (attr != 0 &&
+ pthread_attr_getdetachstate(attr, &si->detached)
+ == PTHREAD_CREATE_DETACHED) {
+ si->detached = TRUE;
+ }
-# if DEBUG_CYGWIN_THREADS
- GC_printf("About to create a thread from 0x%x(0x%x)\n",
- (int)pthread_self(), (int)GetCurrentThreadId);
-# endif
-# if DEBUG_WIN32_PTHREADS
- GC_printf("About to create a thread from 0x%x(0x%x)\n",
- (int)(pthread_self()).p, (int)GetCurrentThreadId());
-# endif
- GC_need_to_lock = TRUE;
- result = pthread_create(new_thread, attr, GC_pthread_start, si);
+# if DEBUG_CYGWIN_THREADS
+ GC_printf("About to create a thread from 0x%x(0x%x)\n",
+ (int)pthread_self(), (int)GetCurrentThreadId);
+# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("About to create a thread from 0x%x(0x%x)\n",
+ (int)(pthread_self()).p, (int)GetCurrentThreadId());
+# endif
+ GC_need_to_lock = TRUE;
+ result = pthread_create(new_thread, attr, GC_pthread_start, si);
- if (result) { /* failure */
- GC_free(si);
+ if (result) { /* failure */
+ GC_free(si);
+ }
+ return(result);
}
- return(result);
}
STATIC void * GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base *sb,
#else /* !GC_PTHREADS */
-# ifndef GC_NO_DLLMAIN
+# ifndef GC_NO_THREADS_DISCOVERY
/* We avoid acquiring locks here, since this doesn't seem to be */
/* preemptible. This may run with an uninitialized collector, in */
/* which case we don't do much. This implies that no threads other */
# endif
static int entry_count = 0;
- if (parallel_initialized && !GC_win32_dll_threads) return TRUE;
+ if (!GC_win32_dll_threads && parallel_initialized) return TRUE;
switch (reason) {
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
/* We are hopefully running in the context of the exiting thread. */
GC_ASSERT(parallel_initialized);
- if (!GC_win32_dll_threads) return TRUE;
- GC_delete_thread(GetCurrentThreadId());
+ if (GC_win32_dll_threads) {
+ GC_delete_thread(GetCurrentThreadId());
+ }
break;
case DLL_PROCESS_DETACH:
- {
+ if (GC_win32_dll_threads) {
int i;
- int my_max;
+ int my_max = (int)GC_get_max_thread_index();
- if (!GC_win32_dll_threads) return TRUE;
- my_max = (int)GC_get_max_thread_index();
for (i = 0; i <= my_max; ++i) {
if (AO_load(&(dll_thread_table[i].tm.in_use)))
GC_delete_gc_thread(dll_thread_table + i);
}
-
GC_deinit();
DeleteCriticalSection(&GC_allocate_ml);
}
break;
-
}
return TRUE;
}
-# endif /* !GC_NO_DLLMAIN */
+# endif /* !GC_NO_THREADS_DISCOVERY */
#endif /* !GC_PTHREADS */