From: ivmai Date: Fri, 22 Oct 2010 05:47:47 +0000 (+0000) Subject: 2010-10-22 Ivan Maidanski X-Git-Tag: gc7_2alpha5-20110107~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=174c1de6e93ac6308dca45063564e175f280d16a;p=gc 2010-10-22 Ivan Maidanski * CMakeLists.txt: Check enable_parallel_mark on Darwin. * configure.ac: Ditto. * darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, DARWIN_QUERY_TASK_THREADS): Rename to GC_NO_THREADS_DISCOVERY and GC_DISCOVER_TASK_THREADS, respectively. * os_dep.c (DARWIN_SUSPEND_GC_THREADS): Ditto. * pthread_support.c (DARWIN_SUSPEND_GC_THREADS): Ditto. * darwin_stop_world.c (DARWIN_QUERY_TASK_THREADS): Don't define (and remove FIXME). * darwin_stop_world.c (GC_use_threads_discovery): Add GC_API; comment; remove FIXME. * win32_threads.c (GC_NO_DLLMAIN): Rename to GC_NO_THREADS_DISCOVERY. * tests/test.c (GC_NO_DLLMAIN): Ditto. * doc/README.macros (GC_NO_DLLMAIN): Ditto. * doc/README.win32 (GC_NO_DLLMAIN): Ditto. * doc/README.macros (GC_NO_THREADS_DISCOVERY): Update the comment. * win32_threads.c (GC_win32_dll_threads): Define as macro to true if GC_DISCOVER_TASK_THREADS (and not GC_NO_THREADS_DISCOVERY); update the comment. * win32_threads.c (GC_use_DllMain): Rename to GC_use_threads_discovery; do not set GC_win32_dll_threads if GC_DISCOVER_TASK_THREADS. * win32_threads.c (GC_started_thread_while_stopped, GC_lookup_thread_inner, UNPROTECT_THREAD, GC_lookup_pthread, GC_thr_init, GC_pthread_create, DllMain): Rewrite some expressions which use GC_win32_dll_threads to minimize the possibility of an "unreachable code" compiler warning when GC_win32_dll_threads is defined as a macro. * win32_threads.c (GC_unregister_my_thread): Don't call GC_delete_thread() if GC_win32_dll_threads and THREAD_LOCAL_ALLOC (since can't happen); use "t" local variable only if not GC_win32_dll_threads. * win32_threads.c (GC_register_my_thread_inner): Reformat the comment. * doc/README.macros (GC_DISCOVER_TASK_THREADS): Document. * include/gc.h (GC_use_DllMain): Rename to GC_use_threads_discovery but keep old name as a macro definition. * include/gc.h (GC_use_threads_discovery): Declare also for Darwin; update the comment. * tests/test.c (main): Call GC_use_threads_discovery for Darwin (to test the mode if possible). * configure: Regenerate. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index ae82e03d..2f775d49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,10 +167,9 @@ IF(CMAKE_USE_PTHREADS_INIT) ADD_DEFINITIONS("-DTHREAD_LOCAL_ALLOC") MESSAGE("Explicit GC_INIT() calls may be required.") SET(SRC ${SRC} darwin_stop_world.c) - # Parallel-mark is currently unreliable on Darwin; ignore request - #IF (${enable_parallel_mark}) - # ADD_DEFINITIONS("-DPARALLEL_MARK") - #ENDIF() + IF (${enable_parallel_mark}) + ADD_DEFINITIONS("-DPARALLEL_MARK") + ENDIF() #TODO #darwin_threads=true ENDIF() diff --git a/ChangeLog b/ChangeLog index 4cb76a03..2dbff978 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,49 @@ +2010-10-22 Ivan Maidanski + + * CMakeLists.txt: Check enable_parallel_mark on Darwin. + * configure.ac: Ditto. + * darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, + DARWIN_QUERY_TASK_THREADS): Rename to GC_NO_THREADS_DISCOVERY and + GC_DISCOVER_TASK_THREADS, respectively. + * os_dep.c (DARWIN_SUSPEND_GC_THREADS): Ditto. + * pthread_support.c (DARWIN_SUSPEND_GC_THREADS): Ditto. + * darwin_stop_world.c (DARWIN_QUERY_TASK_THREADS): Don't define + (and remove FIXME). + * darwin_stop_world.c (GC_use_threads_discovery): Add GC_API; + comment; remove FIXME. + * win32_threads.c (GC_NO_DLLMAIN): Rename to + GC_NO_THREADS_DISCOVERY. + * tests/test.c (GC_NO_DLLMAIN): Ditto. + * doc/README.macros (GC_NO_DLLMAIN): Ditto. + * doc/README.win32 (GC_NO_DLLMAIN): Ditto. + * doc/README.macros (GC_NO_THREADS_DISCOVERY): Update the comment. + * win32_threads.c (GC_win32_dll_threads): Define as macro to true + if GC_DISCOVER_TASK_THREADS (and not GC_NO_THREADS_DISCOVERY); + update the comment. + * win32_threads.c (GC_use_DllMain): Rename to + GC_use_threads_discovery; do not set GC_win32_dll_threads if + GC_DISCOVER_TASK_THREADS. + * win32_threads.c (GC_started_thread_while_stopped, + GC_lookup_thread_inner, UNPROTECT_THREAD, GC_lookup_pthread, + GC_thr_init, GC_pthread_create, DllMain): Rewrite some expressions + which use GC_win32_dll_threads to minimize the possibility of + an "unreachable code" compiler warning when GC_win32_dll_threads + is defined as a macro. + * win32_threads.c (GC_unregister_my_thread): Don't call + GC_delete_thread() if GC_win32_dll_threads and THREAD_LOCAL_ALLOC + (since can't happen); use "t" local variable only if not + GC_win32_dll_threads. + * win32_threads.c (GC_register_my_thread_inner): Reformat the + comment. + * doc/README.macros (GC_DISCOVER_TASK_THREADS): Document. + * include/gc.h (GC_use_DllMain): Rename to + GC_use_threads_discovery but keep old name as a macro definition. + * include/gc.h (GC_use_threads_discovery): Declare also for + Darwin; update the comment. + * tests/test.c (main): Call GC_use_threads_discovery for Darwin + (to test the mode if possible). + * configure: Regenerate. + 2010-10-16 Ivan Maidanski * darwin_stop_world.c (DARWIN_SUSPEND_GC_THREADS, diff --git a/configure b/configure index c3fad5d2..b3c4c544 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac Revision: 1.61 . +# From configure.ac Revision: 1.62 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.67 for gc 7.2alpha5. # @@ -5145,10 +5145,11 @@ $as_echo "$as_me: WARNING: \"Only on NetBSD 2.0 or later.\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&5 $as_echo "$as_me: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&2;}; - # Parallel-mark is currently unreliable on Darwin; ignore request - # if test "${enable_parallel_mark}" = yes; then - # AC_DEFINE(PARALLEL_MARK) - # fi + # Parallel-mark is not well-tested on Darwin + if test "${enable_parallel_mark}" = yes; then + $as_echo "#define PARALLEL_MARK 1" >>confdefs.h + + fi darwin_threads=true ;; *-*-osf*) @@ -5162,7 +5163,7 @@ $as_echo "$as_me: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&2;}; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&5 $as_echo "$as_me: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&2;}; # May want to enable it in other cases, too. - # Measurements havent yet been done. + # Measurements have not yet been done. fi INCLUDES="$INCLUDES -pthread" THREADDLLIBS="-lpthread -lrt" diff --git a/configure.ac b/configure.ac index c98ba41f..748499dd 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ AC_CONFIG_SRCDIR(gcj_mlc.c) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_TARGET AC_PREREQ(2.53) -AC_REVISION($Revision: 1.62 $) +AC_REVISION($Revision: 1.63 $) GC_SET_VERSION AM_INIT_AUTOMAKE([foreign dist-bzip2 nostdinc]) AM_CONFIG_HEADER([include/private/config.h]) @@ -204,10 +204,10 @@ case "$THREADS" in AC_DEFINE(GC_DARWIN_THREADS) AC_DEFINE(THREAD_LOCAL_ALLOC) AC_MSG_WARN("Explicit GC_INIT() calls may be required."); - # Parallel-mark is currently unreliable on Darwin; ignore request - # if test "${enable_parallel_mark}" = yes; then - # AC_DEFINE(PARALLEL_MARK) - # fi + # Parallel-mark is not well-tested on Darwin + if test "${enable_parallel_mark}" = yes; then + AC_DEFINE(PARALLEL_MARK) + fi darwin_threads=true ;; *-*-osf*) @@ -217,7 +217,7 @@ case "$THREADS" in AC_DEFINE(THREAD_LOCAL_ALLOC) AC_MSG_WARN("Explicit GC_INIT() calls may be required."); # May want to enable it in other cases, too. - # Measurements havent yet been done. + # Measurements have not yet been done. fi INCLUDES="$INCLUDES -pthread" THREADDLLIBS="-lpthread -lrt" diff --git a/darwin_stop_world.c b/darwin_stop_world.c index 3e14b635..5f152688 100644 --- a/darwin_stop_world.c +++ b/darwin_stop_world.c @@ -87,26 +87,26 @@ GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) #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 */ @@ -326,7 +326,7 @@ GC_INNER void GC_push_all_stacks(void) 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; @@ -453,7 +453,7 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, return changed; } -#endif /* !DARWIN_SUSPEND_GC_THREADS */ +#endif /* !GC_NO_THREADS_DISCOVERY */ #ifdef MPROTECT_VDB GC_INNER void GC_mprotect_stop(void); @@ -490,7 +490,7 @@ GC_INNER void GC_stop_world(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; @@ -530,7 +530,7 @@ GC_INNER void GC_stop_world(void) 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++) { @@ -601,7 +601,7 @@ GC_INNER void GC_start_world(void) # 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; @@ -646,7 +646,7 @@ GC_INNER void GC_start_world(void) } 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(); diff --git a/doc/README.macros b/doc/README.macros index e9dfb6f5..80bb9f89 100644 --- a/doc/README.macros +++ b/doc/README.macros @@ -437,9 +437,18 @@ ENABLE_TRACE Enables the GC_TRACE=addr environment setting to do its job. DARWIN_DONT_PARSE_STACK Causes the Darwin port to discover thread stack bounds in the same way as other pthread ports, without trying to - walk the frames on the stack. This is recommended only as a fallback for + walk the frames on the stack. This is recommended only as a fall-back for applications that don't support proper stack unwinding. +GC_NO_THREADS_DISCOVERY (Darwin and Win32+DLL only) Exclude DllMain-based + (on Windows) and task-threads-based (on Darwin) thread registration support. + +GC_DISCOVER_TASK_THREADS (Darwin and Win32+DLL only) Compile the collector + with the implicitly turned on task-threads-based (on Darwin) or + DllMain-based (on Windows) approach of threads registering. Only for + compatibility and for the case when it is not possible to call + GC_use_threads_discovery() early (before other GC calls). + USE_PROC_FOR_LIBRARIES Causes the Linux collector to treat writable memory mappings (as reported by /proc) as roots, if it doesn't have other information about them. It no longer traverses dynamic loader @@ -477,9 +486,6 @@ GC_PREFER_MPROTECT_VDB Choose MPROTECT_VDB manually in case of multiple virtual dirty bit strategies are implemented (at present useful on Win32 to force MPROTECT_VDB strategy instead of the default GWW_VDB one). -GC_NO_DLLMAIN (Win32+DLL only) Exclude DllMain-based thread registration - support. - GC_IGNORE_GCJ_INFO Disable GCJ-style type information (useful for debugging on WinCE). diff --git a/doc/README.win32 b/doc/README.win32 index e93df255..2e43b63e 100644 --- a/doc/README.win32 +++ b/doc/README.win32 @@ -181,7 +181,7 @@ This version of the collector by default handles threads similarly to other platforms. James Clark's code which tracks threads attached to the collector DLL still exists, but requires that both - the collector is built in a DLL with GC_DLL defined, and -- GC_use_DllMain() is called before GC initialization, which +- GC_use_threads_discovery() is called before GC initialization, which in turn must happen before creating additional threads. We generally recommend avoiding this if possible, since it seems to be less than 100% reliable. diff --git a/include/gc.h b/include/gc.h index 8019e891..6a3f9ec9 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1032,6 +1032,15 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, /* GC_NO_THREADS is not returned by any GC function anymore. */ #define GC_UNIMPLEMENTED 3 /* Not yet implemented on this platform. */ +#if defined(GC_DARWIN_THREADS) || defined(GC_WIN32_THREADS) + /* Use implicit thread registration and processing (via Win32 DllMain */ + /* or Darwin task_threads). Deprecated. Must be called before */ + /* GC_INIT() and other GC routines. Should be avoided if */ + /* GC_pthread_create, GC_beginthreadex (or GC_CreateThread) could be */ + /* called instead. Disables parallelized GC on Win32. */ + GC_API void GC_CALL GC_use_threads_discovery(void); +#endif + #ifdef GC_THREADS /* Return the signal number (constant) used by the garbage collector */ /* to suspend threads on POSIX systems. Return -1 otherwise. */ @@ -1058,7 +1067,7 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, /* (which redefines some system functions) before calling the system */ /* thread creation function. */ /* It is also always done implicitly on some platforms if */ - /* GC_use_DllMain() is called at start-up. Except for the */ + /* GC_use_threads_discovery() is called at start-up. Except for the */ /* latter case, the explicit call is normally required for threads */ /* created by third-party libraries. */ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *); @@ -1257,7 +1266,7 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( /* (and call GC_unregister_my_thread before thread termination), so */ /* that they will be recorded in the thread table. For backward */ /* compatibility, it is possible to build the GC with GC_DLL */ - /* defined, and to call GC_use_DllMain(). This implicitly */ + /* defined, and to call GC_use_threads_discovery. This implicitly */ /* registers all created threads, but appears to be less robust. */ /* Currently the collector expects all threads to fall through and */ /* terminate normally, or call GC_endthreadex() or GC_ExitThread, */ @@ -1305,11 +1314,8 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( # define WinMain GC_WinMain # endif - /* Use implicit thread registration via DllMain. Deprecated. Must */ - /* be called before GC_INIT() and other GC routines. Should be */ - /* avoided if GC_beginthreadex() or GC_CreateThread() could be called */ - /* instead. */ - GC_API void GC_CALL GC_use_DllMain(void); + /* For compatibility only. */ +# define GC_use_DllMain GC_use_threads_discovery # ifndef GC_NO_THREAD_REDIRECTS # define CreateThread GC_CreateThread diff --git a/os_dep.c b/os_dep.c index 6bbba1b7..becafccd 100644 --- a/os_dep.c +++ b/os_dep.c @@ -3886,7 +3886,7 @@ GC_INNER void GC_mprotect_resume(void) GC_mprotect_thread_notify(ID_RESUME); } -# ifndef DARWIN_SUSPEND_GC_THREADS +# ifndef GC_NO_THREADS_DISCOVERY GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread); # endif @@ -3912,7 +3912,7 @@ STATIC void *GC_mprotect_thread(void *arg) } msg; mach_msg_id_t id; -# if defined(THREADS) && !defined(DARWIN_SUSPEND_GC_THREADS) +# if defined(THREADS) && !defined(GC_NO_THREADS_DISCOVERY) GC_darwin_register_mach_handler_thread(mach_thread_self()); # endif diff --git a/pthread_support.c b/pthread_support.c index beb7e41f..6041a66a 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -321,7 +321,7 @@ static ptr_t marker_sp[MAX_MARKERS - 1] = {0}; static ptr_t marker_bsp[MAX_MARKERS - 1] = {0}; #endif -#if defined(GC_DARWIN_THREADS) && !defined(DARWIN_SUSPEND_GC_THREADS) +#if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) static mach_port_t marker_mach_threads[MAX_MARKERS - 1] = {0}; /* Used only by GC_suspend_thread_list(). */ @@ -348,7 +348,7 @@ STATIC void * GC_mark_thread(void * id) # ifdef IA64 marker_bsp[(word)id] = GC_save_regs_in_stack(); # endif -# if defined(GC_DARWIN_THREADS) && !defined(DARWIN_SUSPEND_GC_THREADS) +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) marker_mach_threads[(word)id] = mach_thread_self(); # endif diff --git a/tests/test.c b/tests/test.c index b221bd35..c2efc82d 100644 --- a/tests/test.c +++ b/tests/test.c @@ -50,7 +50,7 @@ # include "gc_typed.h" # include "private/gc_priv.h" /* For output, locking, MIN_WORDS, */ - /* and some statistics, and gcconfig.h. */ + /* some statistics and gcconfig.h. */ # if defined(MSWIN32) || defined(MSWINCE) # include @@ -1529,9 +1529,11 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, HANDLE win_thr_h; # endif DWORD thread_id; -# if defined(GC_DLL) && !defined(GC_NO_DLLMAIN) && !defined(MSWINCE) \ - && !defined(THREAD_LOCAL_ALLOC) && !defined(PARALLEL_MARK) - GC_use_DllMain(); /* Test with implicit thread registration if possible. */ +# if defined(GC_DLL) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(MSWINCE) && !defined(THREAD_LOCAL_ALLOC) \ + && !defined(PARALLEL_MARK) + GC_use_threads_discovery(); + /* Test with implicit thread registration if possible. */ GC_printf("Using DllMain to track threads\n"); # endif GC_COND_INIT(); @@ -1643,6 +1645,12 @@ int main(void) # ifdef PTW32_STATIC_LIB pthread_win32_process_attach_np (); pthread_win32_thread_attach_np (); +# endif +# if defined(GC_DARWIN_THREADS) && !defined(GC_NO_THREADS_DISCOVERY) \ + && !defined(DARWIN_DONT_PARSE_STACK) && !defined(THREAD_LOCAL_ALLOC) + /* Test with the Darwin implicit thread registration. */ + GC_use_threads_discovery(); + GC_printf("Using Darwin task-threads-based world stop and push\n"); # endif GC_COND_INIT(); diff --git a/win32_threads.c b/win32_threads.c index 05c7c565..9e0876ef 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -90,14 +90,13 @@ /* 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 */ @@ -105,8 +104,22 @@ /* 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. */ @@ -119,18 +132,13 @@ /* 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 */ @@ -150,17 +158,20 @@ GC_INNER GC_bool GC_need_to_lock = FALSE; 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 } @@ -171,8 +182,8 @@ STATIC DWORD GC_main_thread = 0; 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. */ @@ -252,7 +263,7 @@ struct GC_Thread_Rep { 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; @@ -267,7 +278,7 @@ typedef volatile struct GC_Thread_Rep * GC_vthread; * 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 @@ -276,7 +287,7 @@ typedef volatile struct GC_Thread_Rep * GC_vthread; /* 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) { @@ -284,8 +295,8 @@ typedef volatile struct GC_Thread_Rep * GC_vthread; result = AO_load(&GC_attached_thread); if (result) { AO_store(&GC_attached_thread, FALSE); + return TRUE; } - return ((GC_bool)result); } # endif return FALSE; @@ -365,13 +376,11 @@ STATIC GC_thread GC_new_thread(DWORD id) 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) { @@ -389,7 +398,7 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, # 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 */ @@ -471,7 +480,7 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, # 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); @@ -510,7 +519,7 @@ GC_INLINE LONG GC_get_max_thread_index(void) /* 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(); @@ -522,14 +531,10 @@ STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id) 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]; @@ -592,8 +597,8 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void) /* 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); \ } @@ -614,7 +619,7 @@ STATIC void GC_delete_gc_thread(GC_vthread gc_id) # 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 */ @@ -700,7 +705,7 @@ GC_API void GC_CALL GC_allow_register_threads(void) /* 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 @@ -729,18 +734,19 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) 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) { @@ -862,7 +868,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, /* 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(); @@ -875,11 +881,10 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, 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)); @@ -910,7 +915,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, 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 */ @@ -1007,7 +1012,7 @@ GC_INNER void GC_stop_world(void) } # 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 @@ -1022,7 +1027,7 @@ GC_INNER void GC_stop_world(void) 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; @@ -1100,7 +1105,7 @@ GC_INNER void GC_start_world(void) } } } -# if !defined(GC_NO_DLLMAIN) || defined(GC_ASSERTIONS) +# if !defined(GC_NO_THREADS_DISCOVERY) || defined(GC_ASSERTIONS) GC_please_stop = FALSE; # endif } @@ -1305,7 +1310,7 @@ GC_INNER void GC_push_all_stacks(void) 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(); @@ -2269,7 +2274,7 @@ GC_INNER void GC_thr_init(void) 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 @@ -2362,7 +2367,7 @@ GC_INNER void GC_thr_init(void) 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", @@ -2383,43 +2388,43 @@ GC_INNER void GC_thr_init(void) 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, @@ -2547,7 +2552,7 @@ GC_INNER void GC_thr_init(void) #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 */ @@ -2565,7 +2570,7 @@ GC_INNER void GC_thr_init(void) # 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: @@ -2598,31 +2603,28 @@ GC_INNER void GC_thr_init(void) 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 */