diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2012-12-09 22:41:54 +0400 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2012-12-09 22:41:54 +0400 |
commit | 6e5f9a6eabc9c9f09770b51920d09827a23a4309 (patch) | |
tree | bd8ab24c0a6adb7fae3ef634ec396bb57e10be6c | |
parent | 3939770d7751909cdef53c561b5604621e71fd0f (diff) | |
parent | 72092ed8dee8c64ee5b92a06a7628fae085d0cf2 (diff) | |
download | bdwgc-add-long-weakref.tar.gz |
Merge branch 'master' into add-long-weakrefadd-long-weakref
Conflicts:
finalize.c
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | alloc.c | 19 | ||||
-rw-r--r-- | doc/README.macros | 2 | ||||
-rw-r--r-- | finalize.c | 20 | ||||
-rw-r--r-- | include/gc.h | 48 | ||||
-rw-r--r-- | include/gc_mark.h | 16 | ||||
-rw-r--r-- | include/private/gc_priv.h | 86 | ||||
-rw-r--r-- | include/private/gcconfig.h | 18 | ||||
-rw-r--r-- | mallocx.c | 5 | ||||
-rw-r--r-- | misc.c | 182 | ||||
-rw-r--r-- | pthread_stop_world.c | 8 | ||||
-rw-r--r-- | pthread_support.c | 106 | ||||
-rw-r--r-- | reclaim.c | 4 | ||||
-rw-r--r-- | tests/test.c | 57 | ||||
-rw-r--r-- | typd_mlc.c | 2 | ||||
-rw-r--r-- | win32_threads.c | 144 |
17 files changed, 525 insertions, 203 deletions
@@ -168,6 +168,7 @@ Jim Marshall <jim.marshall@wbemsolutions.com> Jim Meyering <jim@meyering.net> Joerg Sonnenberger Johannes Schmidt +Johannes Totz <jtotz@ic.ac.uk> John Bowman John Clements John Ellis @@ -263,6 +264,7 @@ Rainer Orth <ro@cebitec.uni-bielefeld.de> Raja R Harinath <harinath@hurrynot.org> Rauli Ruohonen Regis Cridlig <Regis.Cridlig@cl.cam.ac.uk> +Reimer Behrends <behrends@gmail.com> Rene Girard Rex Dieter <rdieter@math.unl.edu> Reza Shahidi @@ -324,5 +326,6 @@ Wink Saville Xi Wang <xi.wang@gmail.com> Xiaokun Zhu <xiaokun@aero.gla.ac.uk> Yannis Bres +Zach Saw <zach.saw@gmail.com> Zhong Shao Zoltan Varga <vargaz@gmail.com> @@ -67,10 +67,12 @@ cannot write to stderr from GC_gcollect invoked from 'atexit' hook. NetBSD 5.1/x86: threadkey_test hangs sometimes. -Cygwin: gctest: abort with "Too many heap sections" message if SMALL_CONFIG -and PARALLEL_MARK. - Cygwin: subthread_create: exception STATUS_ACCESS_VIOLATION. +Cygwin: gctest: assertion failure at UNLOCK in GC_fork_parent_proc. + +Mingw-w32: gctest: "SuspendThread failed" sometimes occurs (if +GC_DLL+GC_THREADS+GC_ASSERTIONS). + Sun C++ 5.11: test_cpp.cc:237: Error: Too few arguments in call to "operator delete(void*, GCPlacement, extern "C" void(*)(void*,void*), void*)". @@ -111,9 +111,9 @@ GC_API unsigned GC_CALL GC_get_version(void) /* some more variables */ #ifdef GC_DONT_EXPAND - GC_bool GC_dont_expand = TRUE; + int GC_dont_expand = TRUE; #else - GC_bool GC_dont_expand = FALSE; + int GC_dont_expand = FALSE; #endif #ifndef GC_FREE_SPACE_DIVISOR @@ -429,7 +429,7 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) # ifndef SMALL_CONFIG if (GC_print_stats) { GET_TIME(start_time); - GC_log_printf("Initiating full world-stop collection!\n"); + GC_stats_log_printf("Initiating full world-stop collection!\n"); } # endif GC_promote_black_lists(); @@ -468,8 +468,8 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) # ifndef SMALL_CONFIG if (GC_print_stats) { GET_TIME(current_time); - GC_log_printf("Complete collection took %lu msecs\n", - MS_TIME_DIFF(current_time,start_time)); + GC_stats_log_printf("Complete collection took %lu msecs\n", + MS_TIME_DIFF(current_time,start_time)); } # endif return(TRUE); @@ -675,7 +675,7 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) world_stopped_total_divisor = ++divisor; GC_ASSERT(divisor != 0); - GC_log_printf( + GC_stats_log_printf( "World-stopped marking took %lu msecs (%u in average)\n", time_diff, total_time / divisor); } @@ -947,9 +947,10 @@ STATIC void GC_finish_collection(void) GC_print_finalization_stats(); # endif - GC_log_printf("Finalize plus initiate sweep took %lu + %lu msecs\n", - MS_TIME_DIFF(finalize_time,start_time), - MS_TIME_DIFF(done_time,finalize_time)); + GC_stats_log_printf( + "Finalize plus initiate sweep took %lu + %lu msecs\n", + MS_TIME_DIFF(finalize_time,start_time), + MS_TIME_DIFF(done_time,finalize_time)); } # endif } diff --git a/doc/README.macros b/doc/README.macros index b7d72019..a822f0f4 100644 --- a/doc/README.macros +++ b/doc/README.macros @@ -532,6 +532,8 @@ GC_ONLY_LOG_TO_FILE Don't redirect GC stdout and stderr to the log file specified by GC_LOG_FILE environment variable. Has effect only when the variable is set (to anything other than "0"). +GC_ANDROID_LOG (Android only) Output error/debug information to Android log. + GC_DONT_EXPAND Don't expand the heap unless explicitly requested or forced to. GC_USE_ENTIRE_HEAP Causes the non-incremental collector to use the @@ -1093,20 +1093,20 @@ GC_INNER void GC_notify_or_invoke_finalizers(void) struct finalizable_object *fo; unsigned long ready = 0; - GC_log_printf("%lu finalization entries;" - " %lu/%lu short/long disappearing links alive\n", - (unsigned long)GC_fo_entries, - (unsigned long)GC_dl_hashtbl.entries, - (unsigned long)IF_LONG_REFS_PRESENT_ELSE( + GC_stats_log_printf("%lu finalization entries;" + " %lu/%lu short/long disappearing links alive\n", + (unsigned long)GC_fo_entries, + (unsigned long)GC_dl_hashtbl.entries, + (unsigned long)IF_LONG_REFS_PRESENT_ELSE( GC_ll_hashtbl.entries, 0)); for (fo = GC_finalize_now; 0 != fo; fo = fo_next(fo)) ++ready; - GC_log_printf("%lu finalization-ready objects;" - " %ld/%ld short/long links cleared\n", - ready, - (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, - (long)IF_LONG_REFS_PRESENT_ELSE( + GC_stats_log_printf("%lu finalization-ready objects;" + " %ld/%ld short/long links cleared\n", + ready, + (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, + (long)IF_LONG_REFS_PRESENT_ELSE( GC_old_ll_entries - GC_ll_hashtbl.entries, 0)); } #endif /* !SMALL_CONFIG */ diff --git a/include/gc.h b/include/gc.h index 83444af4..7b9cbfe4 100644 --- a/include/gc.h +++ b/include/gc.h @@ -365,12 +365,29 @@ GC_API int GC_CALL GC_get_pages_executable(void); /* Overrides the default handle-fork mode. Non-zero value means GC */ /* should install proper pthread_atfork handlers. Has effect only if */ -/* called before GC_INIT. Clients should invoke GC_set_handle_fork(1) */ -/* only if going to use fork with GC functions called in the forked */ -/* child. (Note that such client and atfork handlers activities are */ -/* not fully POSIX-compliant.) */ +/* called before GC_INIT. Clients should invoke GC_set_handle_fork */ +/* with non-zero argument if going to use fork with GC functions called */ +/* in the forked child. (Note that such client and atfork handlers */ +/* activities are not fully POSIX-compliant.) GC_set_handle_fork */ +/* instructs GC_init to setup GC fork handlers using pthread_atfork, */ +/* the latter might fail (or, even, absent on some targets) causing */ +/* abort at GC initialization. Starting from 7.3alpha3, problems with */ +/* missing (or failed) pthread_atfork() could be avoided by invocation */ +/* of GC_set_handle_fork(-1) at application start-up and surrounding */ +/* each fork() with the relevant GC_atfork_prepare/parent/child calls. */ GC_API void GC_CALL GC_set_handle_fork(int); +/* Routines to handle POSIX fork() manually (no-op if handled */ +/* automatically). GC_atfork_prepare should be called immediately */ +/* before fork(); GC_atfork_parent should be invoked just after fork in */ +/* the branch that corresponds to parent process (i.e., fork result is */ +/* non-zero); GC_atfork_child is to be called immediately in the child */ +/* branch (i.e., fork result is 0). Note that GC_atfork_child() call */ +/* should, of course, precede GC_start_mark_threads call (if any). */ +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); + /* Initialize the collector. Portable clients should call GC_INIT() */ /* from the main program instead. */ GC_API void GC_CALL GC_init(void); @@ -1243,6 +1260,10 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, /* systems. Return -1 otherwise. */ GC_API int GC_CALL GC_get_thr_restart_signal(void); + /* Restart marker threads after POSIX fork in child. Meaningless in */ + /* other situations. Should not be called if fork followed by exec. */ + GC_API void GC_CALL GC_start_mark_threads(void); + /* Explicitly enable GC_register_my_thread() invocation. */ /* Done implicitly if a GC thread-creation function is called (or */ /* implicit thread registration is activated). Otherwise, it must */ @@ -1451,7 +1472,7 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( /* Note: for Cygwin and win32-pthread, this is skipped */ /* unless windows.h is included before gc.h. */ -# ifndef GC_NO_THREAD_DECLS +# if !defined(GC_NO_THREAD_DECLS) || defined(GC_BUILD) # ifdef __cplusplus } /* Including windows.h in an extern "C" context no longer works. */ @@ -1483,6 +1504,14 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( LPVOID /* reserved */); # endif /* GC_INSIDE_DLL */ +# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \ + && !defined(UINTPTR_MAX) + typedef GC_word GC_uintptr_t; +# else + typedef uintptr_t GC_uintptr_t; +# endif +# define GC_WIN32_SIZE_T GC_uintptr_t + /* All threads must be created using GC_CreateThread or */ /* GC_beginthreadex, or must explicitly call GC_register_my_thread */ /* (and call GC_unregister_my_thread before thread termination), so */ @@ -1495,7 +1524,7 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( /* so that the thread is properly unregistered. */ GC_API HANDLE WINAPI GC_CreateThread( LPSECURITY_ATTRIBUTES /* lpThreadAttributes */, - DWORD /* dwStackSize */, + GC_WIN32_SIZE_T /* dwStackSize */, LPTHREAD_START_ROUTINE /* lpStartAddress */, LPVOID /* lpParameter */, DWORD /* dwCreationFlags */, LPDWORD /* lpThreadId */); @@ -1509,13 +1538,6 @@ GC_API void GC_CALL GC_register_has_static_roots_callback( DWORD /* dwExitCode */); # if !defined(_WIN32_WCE) && !defined(__CEGCC__) -# if !defined(_UINTPTR_T) && !defined(_UINTPTR_T_DEFINED) \ - && !defined(UINTPTR_MAX) - typedef GC_word GC_uintptr_t; -# else - typedef uintptr_t GC_uintptr_t; -# endif - GC_API GC_uintptr_t GC_CALL GC_beginthreadex( void * /* security */, unsigned /* stack_size */, unsigned (__stdcall *)(void *), diff --git a/include/gc_mark.h b/include/gc_mark.h index 508fb2cc..4c003c03 100644 --- a/include/gc_mark.h +++ b/include/gc_mark.h @@ -183,14 +183,24 @@ GC_API unsigned GC_CALL GC_new_kind_inner(void ** /* free_list */, GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc); GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc); -/* Allocate an object of a given kind. Note that in multithreaded */ +/* Allocate an object of a given kind. By default, there are only */ +/* a few kinds: composite (pointer-free), atomic, uncollectable, etc. */ +/* We claim it is possible for clever client code that understands the */ +/* GC internals to add more, e.g. to communicate object layout */ +/* information to the collector. Note that in the multi-threaded */ /* contexts, this is usually unsafe for kinds that have the descriptor */ /* in the object itself, since there is otherwise a window in which */ /* the descriptor is not correct. Even in the single-threaded case, */ /* we need to be sure that cleared objects on a free list don't */ /* cause a GC crash if they are accidentally traced. */ -GC_API GC_ATTR_MALLOC void * GC_CALL - GC_generic_malloc(size_t /* lb */, int /* k */); +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc(size_t /* lb */, + int /* k */); + +GC_API GC_ATTR_MALLOC void * GC_CALL GC_generic_malloc_ignore_off_page( + size_t /* lb */, int /* k */); + /* As above, but pointers to past the */ + /* first page of the resulting object */ + /* are ignored. */ typedef void (GC_CALLBACK * GC_describe_type_fn)(void * /* p */, char * /* out_buf */); diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 6352267d..c3125170 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -476,9 +476,10 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # elif defined(MSWINCE) && defined(NO_DEBUGGING) # define ABORT(msg) (GC_on_abort(msg), ExitProcess(-1)) # elif defined(MSWIN32) || defined(MSWINCE) -# define ABORT(msg) (GC_on_abort(msg), DebugBreak()) - /* Note that on a WinCE box, this could be silently */ - /* ignored (i.e., the program is not aborted). */ +# define ABORT(msg) { GC_on_abort(msg); DebugBreak(); } + /* Note that: on a WinCE box, this could be silently */ + /* ignored (i.e., the program is not aborted); */ + /* DebugBreak is a statement in some toolchains. */ # else # define ABORT(msg) (GC_on_abort(msg), abort()) # endif /* !MSWIN32 */ @@ -1027,7 +1028,11 @@ struct roots { # define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ # endif # elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES) -# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */ +# if defined(PARALLEL_MARK) && (defined(MSWIN32) || defined(CYGWIN32)) +# define MAX_HEAP_SECTS 384 +# else +# define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */ +# endif # elif CPP_WORDSZ > 32 # define MAX_HEAP_SECTS 1024 /* Roughly 8GB */ # else @@ -1764,23 +1769,10 @@ GC_INNER void GC_collect_a_little_inner(int n); /* collection work, if appropriate. */ /* A unit is an amount appropriate for */ /* HBLKSIZE bytes of allocation. */ -/* void * GC_generic_malloc(size_t lb, int k); */ - /* Allocate an object of the given */ - /* kind. By default, there are only */ - /* a few kinds: composite(pointerfree), */ - /* atomic, uncollectable, etc. */ - /* We claim it's possible for clever */ - /* client code that understands GC */ - /* internals to add more, e.g. to */ - /* communicate object layout info */ - /* to the collector. */ - /* The actual decl is in gc_mark.h. */ -GC_INNER void * GC_generic_malloc_ignore_off_page(size_t b, int k); - /* As above, but pointers past the */ - /* first page of the resulting object */ - /* are ignored. */ + GC_INNER void * GC_generic_malloc_inner(size_t lb, int k); - /* Ditto, but I already hold lock, etc. */ + /* Allocate an object of the given */ + /* kind but assuming lock already held. */ GC_INNER void * GC_generic_malloc_inner_ignore_off_page(size_t lb, int k); /* Allocate an object, where */ /* the client guarantees that there */ @@ -1885,16 +1877,25 @@ GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ /* occasionally. It is ok to read it */ /* without acquiring the lock. */ +#define VERBOSE 2 #ifndef SMALL_CONFIG /* GC_print_stats should be visible to extra/MacOS.c. */ - extern int GC_print_stats; /* Nonzero generates basic GC log. */ +# ifndef GC_ANDROID_LOG + extern int GC_print_stats; /* Nonzero generates basic GC log. */ /* VERBOSE generates add'l messages. */ -#else +# define GC_real_print_stats GC_print_stats +# else +# ifndef GC_print_stats +# define GC_print_stats VERBOSE +# endif + extern int GC_real_print_stats; + /* Influences logging only if redirected to a file. */ +# endif +#else /* SMALL_CONFIG */ # define GC_print_stats 0 /* Will this remove the message character strings from the executable? */ /* With a particular level of optimizations, it should... */ #endif -#define VERBOSE 2 #ifdef KEEP_BACK_PTRS GC_EXTERN long GC_backtraces; @@ -1948,7 +1949,20 @@ GC_EXTERN GC_bool GC_print_back_height; #endif #ifdef CAN_HANDLE_FORK - GC_EXTERN GC_bool GC_handle_fork; + GC_EXTERN int GC_handle_fork; + /* Fork-handling mode: */ + /* 0 means no fork handling requested (but client could */ + /* anyway call fork() provided it is surrounded with */ + /* GC_atfork_prepare/parent/child calls); */ + /* -1 means GC tries to use pthread_at_fork if it is */ + /* available (if it succeeds then GC_handle_fork value */ + /* is changed to 1), client should nonetheless surround */ + /* fork() with GC_atfork_prepare/parent/child (for the */ + /* case of pthread_at_fork failure or absence); */ + /* 1 (or other values) means client fully relies on */ + /* pthread_at_fork (so if it is missing or failed then */ + /* abort occurs in GC_init), GC_atfork_prepare and the */ + /* accompanying routines are no-op in such a case. */ #endif #ifndef GC_DISABLE_INCREMENTAL @@ -2018,7 +2032,8 @@ GC_API void GC_CALL GC_noop1(word); /* Logging and diagnostic output: */ /* GC_printf is used typically on client explicit print requests. */ -/* It's recommended to put "\n" at 'format' string end (for atomicity). */ +/* For all GC_X_printf routines, it is recommended to put "\n" at */ +/* 'format' string end (for output atomicity). */ GC_API_PRIV void GC_printf(const char * format, ...) GC_ATTR_FORMAT_PRINTF(1, 2); /* A version of printf that doesn't allocate, */ @@ -2028,20 +2043,33 @@ GC_API_PRIV void GC_printf(const char * format, ...) GC_API_PRIV void GC_err_printf(const char * format, ...) GC_ATTR_FORMAT_PRINTF(1, 2); +/* Basic logging routine. Typically, GC_log_printf is called directly */ +/* only inside various DEBUG_x blocks. */ #if defined(__cplusplus) && defined(SYMBIAN) extern "C" { #endif -/* Logging routine. Typically called only if GC_print_stats. It is */ -/* recommended to put "\n" at 'format' string end (for atomicity). */ GC_API_PRIV void GC_log_printf(const char * format, ...) GC_ATTR_FORMAT_PRINTF(1, 2); #if defined(__cplusplus) && defined(SYMBIAN) } #endif -#define GC_COND_LOG_PRINTF if (!GC_print_stats) {} else GC_log_printf +#ifndef GC_ANDROID_LOG + /* GC_stats_log_printf should be called only if GC_print_stats. */ +# define GC_stats_log_printf GC_log_printf + /* GC_verbose_log_printf is called only if GC_print_stats is VERBOSE. */ +# define GC_verbose_log_printf GC_log_printf +#else + GC_INNER void GC_stats_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); + GC_INNER void GC_verbose_log_printf(const char *format, ...) + GC_ATTR_FORMAT_PRINTF(1, 2); +#endif /* GC_ANDROID_LOG */ + +/* Convenient macros for GC_stats/verbose_log_printf invocation. */ +#define GC_COND_LOG_PRINTF if (!GC_print_stats) {} else GC_stats_log_printf #define GC_VERBOSE_LOG_PRINTF \ - if (GC_print_stats != VERBOSE) {} else GC_log_printf + if (GC_print_stats != VERBOSE) {} else GC_verbose_log_printf void GC_err_puts(const char *s); /* Write s to stderr, don't buffer, don't add */ diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index a35f4edb..c21c38ca 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -2656,15 +2656,27 @@ #endif #if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \ - && ((defined(GC_PTHREADS) && !defined(HURD) && !defined(NACL) \ - && !defined(PLATFORM_ANDROID) && !defined(GC_WIN32_PTHREADS) \ - && !defined(USE_WINALLOC)) \ + && !defined(HAVE_NO_FORK) \ + && ((defined(GC_PTHREADS) && !defined(NACL) \ + && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \ || (defined(DARWIN) && defined(MPROTECT_VDB)) || defined(HANDLE_FORK)) /* Attempts (where supported and requested) to make GC_malloc work in */ /* a child process fork'ed from a multi-threaded parent. */ # define CAN_HANDLE_FORK #endif +#if defined(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \ + && !defined(HURD) && !defined(PLATFORM_ANDROID) + /* Have working pthread_atfork(). */ +# define CAN_CALL_ATFORK +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \ + && (defined(MSWIN32) || defined(MSWINCE) || defined(DOS4GW) \ + || defined(OS2) || defined(SYMBIAN) /* and probably others ... */) +# define HAVE_NO_FORK +#endif + #if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ && defined(PARALLEL_MARK) /* Minimize compare-and-swap usage. */ @@ -165,11 +165,10 @@ void * realloc(void * p, size_t lb) # undef GC_debug_realloc_replacement # endif /* REDIRECT_REALLOC */ - /* Allocate memory such that only pointers to near the */ /* beginning of the object are considered. */ -/* We avoid holding allocation lock while we clear memory. */ -GC_INNER void * GC_generic_malloc_ignore_off_page(size_t lb, int k) +/* We avoid holding allocation lock while we clear the memory. */ +GC_API void * GC_CALL GC_generic_malloc_ignore_off_page(size_t lb, int k) { void *result; size_t lg; @@ -84,14 +84,14 @@ ptr_t GC_stackbottom = 0; ptr_t GC_register_stackbottom = 0; #endif -GC_bool GC_dont_gc = 0; +int GC_dont_gc = FALSE; -GC_bool GC_dont_precollect = 0; +int GC_dont_precollect = FALSE; GC_bool GC_quiet = 0; /* used also in pcr_interface.c */ #ifndef SMALL_CONFIG - GC_bool GC_print_stats = 0; + int GC_real_print_stats = 0; #endif #ifdef GC_PRINT_BACK_HEIGHT @@ -171,24 +171,48 @@ GC_oom_func GC_oom_fn = GC_default_oom_fn; #ifdef CAN_HANDLE_FORK # ifdef HANDLE_FORK - GC_INNER GC_bool GC_handle_fork = TRUE; + GC_INNER int GC_handle_fork = 1; /* The value is examined by GC_thr_init. */ # else - GC_INNER GC_bool GC_handle_fork = FALSE; + GC_INNER int GC_handle_fork = FALSE; # endif -#endif /* CAN_HANDLE_FORK */ -/* Overrides the default handle-fork mode. Non-zero value means GC */ -/* should install proper pthread_atfork handlers (or abort if not */ -/* supported). Has effect only if called before GC_INIT. */ +#elif !defined(HAVE_NO_FORK) + + /* Same as above but with GC_CALL calling conventions. */ + GC_API void GC_CALL GC_atfork_prepare(void) + { +# ifdef THREADS + ABORT("fork() handling unsupported"); +# endif + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + /* empty */ + } + + GC_API void GC_CALL GC_atfork_child(void) + { + /* empty */ + } +#endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */ + +/* Overrides the default automatic handle-fork mode. Has effect only */ +/* if called before GC_INIT. */ GC_API void GC_CALL GC_set_handle_fork(int value GC_ATTR_UNUSED) { # ifdef CAN_HANDLE_FORK if (!GC_is_initialized) - GC_handle_fork = (GC_bool)value; + GC_handle_fork = value >= -1 ? value : 1; + /* Map all negative values except for -1 to a positive one. */ # elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB)) - if (!GC_is_initialized && value) - ABORT("fork() handling disabled"); + if (!GC_is_initialized && value) { +# ifndef SMALL_CONFIG + GC_init(); /* just to initialize GC_stderr */ +# endif + ABORT("fork() handling unsupported"); + } # else /* No at-fork handler is needed in the single-threaded mode. */ # endif @@ -746,10 +770,13 @@ STATIC void GC_exit_check(void) # define maybe_install_looping_handler() #endif +#define GC_DEFAULT_STDOUT_FD 1 +#define GC_DEFAULT_STDERR_FD 2 + #if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE) - STATIC int GC_stdout = 1; - STATIC int GC_stderr = 2; - STATIC int GC_log = 2; /* stderr */ + STATIC int GC_stdout = GC_DEFAULT_STDOUT_FD; + STATIC int GC_stderr = GC_DEFAULT_STDERR_FD; + STATIC int GC_log = GC_DEFAULT_STDERR_FD; #endif STATIC word GC_parse_mem_size_arg(const char *str) @@ -857,12 +884,12 @@ GC_API void GC_CALL GC_init(void) # ifdef GC_PRINT_VERBOSE_STATS /* This is useful for debugging and profiling on platforms with */ /* missing getenv() (like WinCE). */ - GC_print_stats = VERBOSE; + GC_real_print_stats = VERBOSE; # else if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) { - GC_print_stats = VERBOSE; + GC_real_print_stats = VERBOSE; } else if (0 != GETENV("GC_PRINT_STATS")) { - GC_print_stats = 1; + GC_real_print_stats = 1; } # endif # if defined(UNIX_LIKE) || defined(CYGWIN32) || defined(SYMBIAN) @@ -1228,6 +1255,14 @@ GC_API void GC_CALL GC_enable_incremental(void) GC_init(); } +#if defined(THREADS) && (!defined(PARALLEL_MARK) || !defined(CAN_HANDLE_FORK)) + GC_API void GC_CALL GC_start_mark_threads(void) + { + /* No action since parallel markers are disabled (or no POSIX fork). */ + GC_ASSERT(I_DONT_HOLD_LOCK()); + } +#endif + #if defined(MSWIN32) || defined(MSWINCE) # if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE) @@ -1436,6 +1471,14 @@ GC_API void GC_CALL GC_enable_incremental(void) # define WRITE(f, buf, len) GC_write(f, buf, len) #endif /* !MSWIN32 && !OS2 && !MACOS */ +#ifdef GC_ANDROID_LOG +# include <android/log.h> + +# ifndef GC_ANDROID_LOG_TAG +# define GC_ANDROID_LOG_TAG "BDWGC" +# endif +#endif + #define BUFSZ 1024 #ifdef NO_VSNPRINTF @@ -1455,45 +1498,111 @@ GC_API void GC_CALL GC_enable_incremental(void) /* Floating point arguments and formats should be avoided, since FP */ /* conversion is more likely to allocate memory. */ /* Assumes that no more than BUFSZ-1 characters are written at once. */ -#define GC_PRINTF_IMPL(f, f_name, format) { \ +#define GC_PRINTF_FILLBUF(buf, format) { \ va_list args; \ - char buf[BUFSZ + 1]; \ va_start(args, format); \ - buf[BUFSZ] = 0x15; \ - (void)vsnprintf(buf, BUFSZ, format, args); \ + (buf)[sizeof(buf) - 1] = 0x15; /* guard */ \ + (void)vsnprintf(buf, sizeof(buf) - 1, format, args); \ va_end(args); \ - if (buf[BUFSZ] != 0x15) \ + if ((buf)[sizeof(buf) - 1] != 0x15) \ ABORT("GC_printf clobbered stack"); \ - if (WRITE(f, buf, strlen(buf)) < 0) \ - ABORT("write to " f_name " failed"); \ } void GC_printf(const char *format, ...) { - if (GC_quiet) return; + char buf[BUFSZ + 1]; - GC_PRINTF_IMPL(GC_stdout, "stdout", format); +# ifdef GC_ANDROID_LOG + GC_PRINTF_FILLBUF(buf, format); + __android_log_write(ANDROID_LOG_DEBUG, GC_ANDROID_LOG_TAG, buf); + if (GC_stdout == GC_DEFAULT_STDOUT_FD) + return; /* skip duplicate write to stdout */ +# endif + if (!GC_quiet) { +# ifndef GC_ANDROID_LOG + GC_PRINTF_FILLBUF(buf, format); +# endif + if (WRITE(GC_stdout, buf, strlen(buf)) < 0) + ABORT("write to stdout failed"); + } } void GC_err_printf(const char *format, ...) { - GC_PRINTF_IMPL(GC_stderr, "stderr", format); -} + char buf[BUFSZ + 1]; -void GC_log_printf(const char *format, ...) -{ - GC_PRINTF_IMPL(GC_log, "log", format); + GC_PRINTF_FILLBUF(buf, format); + GC_err_puts(buf); } -/* This is equivalent to GC_err_printf("%s",s). */ +#ifndef GC_ANDROID_LOG + + void GC_log_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + if (WRITE(GC_log, buf, strlen(buf)) < 0) + ABORT("write to GC log failed"); + } + +# define GC_warn_printf GC_err_printf + +#else + +# define GC_LOG_PRINTF_IMPL(loglevel, fileLogCond, format) \ + { \ + char buf[BUFSZ + 1]; \ + GC_PRINTF_FILLBUF(buf, format); \ + __android_log_write(loglevel, GC_ANDROID_LOG_TAG, buf); \ + if (GC_log != GC_DEFAULT_STDERR_FD && (fileLogCond) \ + && WRITE(GC_log, buf, strlen(buf)) < 0) \ + ABORT("write to GC log file failed"); \ + } + + void GC_log_printf(const char *format, ...) + { + GC_LOG_PRINTF_IMPL(ANDROID_LOG_DEBUG, TRUE, format); + } + + GC_INNER void GC_stats_log_printf(const char *format, ...) + { + GC_LOG_PRINTF_IMPL(ANDROID_LOG_INFO, GC_real_print_stats != 0, format); + } + + GC_INNER void GC_verbose_log_printf(const char *format, ...) + { + GC_LOG_PRINTF_IMPL(ANDROID_LOG_VERBOSE, GC_real_print_stats == VERBOSE, + format); + } + + STATIC void GC_warn_printf(const char *format, ...) + { + char buf[BUFSZ + 1]; + + GC_PRINTF_FILLBUF(buf, format); + __android_log_write(ANDROID_LOG_WARN, GC_ANDROID_LOG_TAG, buf); + if (GC_real_print_stats && GC_stderr != GC_DEFAULT_STDERR_FD + && WRITE(GC_stderr, buf, strlen(buf)) < 0) + ABORT("write to stderr failed"); + } + +#endif /* GC_ANDROID_LOG */ + void GC_err_puts(const char *s) { +# ifdef GC_ANDROID_LOG + __android_log_write(ANDROID_LOG_ERROR, GC_ANDROID_LOG_TAG, s); + if (GC_stderr == GC_DEFAULT_STDERR_FD) + return; /* skip duplicate write to stderr */ +# endif if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed"); } STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg) { - GC_err_printf(msg, arg); + /* TODO: Add assertion on arg comply with msg (format). */ + GC_warn_printf(msg, arg); } GC_INNER GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; @@ -1574,9 +1683,12 @@ GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void) if (WRITE(GC_stderr, (void *)msg, strlen(msg)) >= 0) (void)WRITE(GC_stderr, (void *)("\n"), 1); } +# ifdef GC_ANDROID_LOG + __android_log_assert("*" /* cond */, GC_ANDROID_LOG_TAG, "%s\n", msg); +# endif } -# ifndef NO_DEBUGGING +# if !defined(NO_DEBUGGING) && !defined(GC_ANDROID_LOG) if (GETENV("GC_LOOP_ON_ABORT") != NULL) { /* In many cases it's easier to debug a running process. */ /* It's arguably nicer to sleep, but that makes it harder */ diff --git a/pthread_stop_world.c b/pthread_stop_world.c index cbae189c..a06ffb71 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -493,6 +493,10 @@ STATIC int GC_suspend_all(void) case 0: break; default: +# ifdef DEBUG_THREADS + GC_log_printf("pthread_kill failed at suspend," + " errcode=%d\n", result); +# endif ABORT("pthread_kill failed"); } # endif @@ -816,6 +820,10 @@ GC_INNER void GC_start_world(void) case 0: break; default: +# ifdef DEBUG_THREADS + GC_log_printf("pthread_kill failed at resume," + " errcode=%d\n", result); +# endif ABORT("pthread_kill failed"); } # endif diff --git a/pthread_support.c b/pthread_support.c index a9baa57e..da24d10e 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -390,16 +390,28 @@ STATIC void * GC_mark_thread(void * id) STATIC pthread_t GC_mark_threads[MAX_MARKERS]; -static void start_mark_threads(void) +#ifdef CAN_HANDLE_FORK + static int available_markers_m1 = 0; +# define start_mark_threads GC_start_mark_threads + GC_API void GC_CALL +#else +# define available_markers_m1 GC_markers_m1 + static void +#endif +start_mark_threads(void) { int i; pthread_attr_t attr; GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef CAN_HANDLE_FORK + if (available_markers_m1 <= 0 || GC_parallel) return; + /* Skip if parallel markers disabled or already started. */ +# endif + INIT_REAL_SYMS(); /* for pthread_create */ if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); - if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) ABORT("pthread_attr_setdetachstate failed"); @@ -410,7 +422,6 @@ static void start_mark_threads(void) # define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word)) { size_t old_size; - int code; if (pthread_attr_getstacksize(&attr, &old_size) != 0) ABORT("pthread_attr_getstacksize failed"); @@ -420,18 +431,18 @@ static void start_mark_threads(void) } } # endif /* HPUX || GC_DGUX386_THREADS */ - for (i = 0; i < GC_markers_m1; ++i) { + for (i = 0; i < available_markers_m1; ++i) { if (0 != REAL_FUNC(pthread_create)(GC_mark_threads + i, &attr, GC_mark_thread, (void *)(word)i)) { WARN("Marker thread creation failed, errno = %" WARN_PRIdPTR "\n", errno); /* Don't try to create other marker threads. */ - GC_markers_m1 = i; break; } } - GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); + GC_markers_m1 = i; pthread_attr_destroy(&attr); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); } #endif /* PARALLEL_MARK */ @@ -666,6 +677,8 @@ STATIC void GC_remove_all_threads_but_me(void) /* GC_destroy_thread_local and GC_free_internal */ /* before update). */ me -> stop_info.mach_thread = mach_thread_self(); +# elif defined(PLATFORM_ANDROID) + me -> kernel_id = gettid(); # endif # if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC) /* Some TLS implementations might be not fork-friendly, so */ @@ -930,7 +943,7 @@ IF_CANCEL(static int fork_cancel_state;) /* protected by allocation lock. */ /* Called before a fork() */ -STATIC void GC_fork_prepare_proc(void) +static void fork_prepare_proc(void) { /* Acquire all relevant locks, so that after releasing the locks */ /* the child will see a consistent state in which monitor */ @@ -953,8 +966,8 @@ STATIC void GC_fork_prepare_proc(void) # endif } -/* Called in parent after a fork() */ -STATIC void GC_fork_parent_proc(void) +/* Called in parent after a fork() (even if the latter failed). */ +static void fork_parent_proc(void) { # if defined(PARALLEL_MARK) if (GC_parallel) @@ -965,7 +978,7 @@ STATIC void GC_fork_parent_proc(void) } /* Called in child after a fork() */ -STATIC void GC_fork_child_proc(void) +static void fork_child_proc(void) { /* Clean up the thread table, so that just our thread is left. */ # if defined(PARALLEL_MARK) @@ -981,6 +994,31 @@ STATIC void GC_fork_child_proc(void) RESTORE_CANCEL(fork_cancel_state); UNLOCK(); } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { +# if defined(GC_DARWIN_THREADS) && defined(MPROTECT_VDB) + if (GC_dirty_maintained) { + GC_ASSERT(0 == GC_handle_fork); + ABORT("Unable to fork while mprotect_thread is running"); + } +# endif + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } #endif /* CAN_HANDLE_FORK */ #ifdef INCLUDE_LINUX_THREAD_DESCR @@ -998,10 +1036,17 @@ GC_INNER void GC_thr_init(void) GC_ASSERT((word)&GC_threads % sizeof(word) == 0); # ifdef CAN_HANDLE_FORK /* Prepare for forks if requested. */ - if (GC_handle_fork - && pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc, - GC_fork_child_proc) != 0) - ABORT("pthread_atfork failed"); + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } # endif # ifdef INCLUDE_LINUX_THREAD_DESCR /* Explicitly register the region including the address */ @@ -1056,36 +1101,37 @@ GC_INNER void GC_thr_init(void) WARN("GC_get_nprocs() returned %" WARN_PRIdPTR "\n", GC_nprocs); GC_nprocs = 2; /* assume dual-core */ # ifdef PARALLEL_MARK - GC_parallel = FALSE; /* but use only one marker */ + available_markers_m1 = 0; /* but use only one marker */ # endif } else { # ifdef PARALLEL_MARK { char * markers_string = GETENV("GC_MARKERS"); + int markers_m1; + if (markers_string != NULL) { - GC_markers_m1 = atoi(markers_string) - 1; - if (GC_markers_m1 >= MAX_MARKERS) { + markers_m1 = atoi(markers_string) - 1; + if (markers_m1 >= MAX_MARKERS) { WARN("Limiting number of mark threads\n", 0); - GC_markers_m1 = MAX_MARKERS - 1; + markers_m1 = MAX_MARKERS - 1; } } else { - GC_markers_m1 = GC_nprocs - 1; + markers_m1 = GC_nprocs - 1; # ifdef GC_MIN_MARKERS /* This is primarily for targets without getenv(). */ - if (GC_markers_m1 < GC_MIN_MARKERS - 1) - GC_markers_m1 = GC_MIN_MARKERS - 1; + if (markers_m1 < GC_MIN_MARKERS - 1) + markers_m1 = GC_MIN_MARKERS - 1; # endif - if (GC_markers_m1 >= MAX_MARKERS) - GC_markers_m1 = MAX_MARKERS - 1; /* silently limit the value */ + if (markers_m1 >= MAX_MARKERS) + markers_m1 = MAX_MARKERS - 1; /* silently limit the value */ } + available_markers_m1 = markers_m1; } # endif } + GC_COND_LOG_PRINTF("Number of processors = %d\n", GC_nprocs); # ifdef PARALLEL_MARK - GC_COND_LOG_PRINTF("Number of processors = %d," - " number of marker threads = %d\n", - GC_nprocs, GC_markers_m1 + 1); - if (GC_markers_m1 <= 0) { + if (available_markers_m1 <= 0) { /* Disable parallel marking. */ GC_parallel = FALSE; GC_COND_LOG_PRINTF( @@ -1093,13 +1139,9 @@ GC_INNER void GC_thr_init(void) } else { /* Disable true incremental collection, but generational is OK. */ GC_time_limit = GC_TIME_UNLIMITED; - } - /* If we are using a parallel marker, actually start helper threads. */ - if (GC_parallel) { + /* If we are using a parallel marker, actually start helper threads. */ start_mark_threads(); } -# else - GC_COND_LOG_PRINTF("Number of processors = %d\n", GC_nprocs); # endif } @@ -727,8 +727,8 @@ GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) # ifndef SMALL_CONFIG if (GC_print_stats == VERBOSE) { GET_TIME(done_time); - GC_log_printf("Disposing of reclaim lists took %lu msecs\n", - MS_TIME_DIFF(done_time,start_time)); + GC_verbose_log_printf("Disposing of reclaim lists took %lu msecs\n", + MS_TIME_DIFF(done_time,start_time)); } # endif return(TRUE); diff --git a/tests/test.c b/tests/test.c index 102cabd2..15c0e110 100644 --- a/tests/test.c +++ b/tests/test.c @@ -89,14 +89,24 @@ # if (!defined(THREADS) || !defined(HANDLE_FORK) \ || (defined(DARWIN) && defined(MPROTECT_VDB) \ && !defined(NO_INCREMENTAL) && !defined(MAKE_BACK_GRAPH))) \ - && !defined(NO_TEST_HANDLE_FORK) + && !defined(NO_TEST_HANDLE_FORK) && !defined(TEST_HANDLE_FORK) \ + && !defined(TEST_FORK_WITHOUT_ATFORK) # define NO_TEST_HANDLE_FORK # endif # ifndef NO_TEST_HANDLE_FORK # include <unistd.h> -# define INIT_FORK_SUPPORT GC_set_handle_fork(1) -# else +# ifdef HANDLE_FORK +# define INIT_FORK_SUPPORT GC_set_handle_fork(1) + /* Causes abort in GC_init on pthread_atfork failure. */ +# elif !defined(TEST_FORK_WITHOUT_ATFORK) +# define INIT_FORK_SUPPORT GC_set_handle_fork(-1) + /* Passing -1 implies fork() should be as well manually */ + /* surrounded with GC_atfork_prepare/parent/child. */ +# endif +# endif + +# ifndef INIT_FORK_SUPPORT # define INIT_FORK_SUPPORT /* empty */ # endif @@ -522,7 +532,10 @@ void check_marks_int_list(sexpr x) { DWORD thread_id; HANDLE h; - h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id); + h = GC_CreateThread((SECURITY_ATTRIBUTES *)NULL, (word)0, + tiny_reverse_test, NULL, (DWORD)0, &thread_id); + /* Explicitly specify types of the */ + /* arguments to test the prototype. */ if (h == (HANDLE)NULL) { GC_printf("Small thread creation failed %d\n", (int)GetLastError()); @@ -1298,6 +1311,32 @@ void run_one_test(void) GC_free(GC_malloc_atomic(0)); GC_free(GC_malloc(0)); GC_free(GC_malloc_atomic(0)); +# ifndef NO_TEST_HANDLE_FORK + GC_atfork_prepare(); + if (fork() != 0) { + GC_atfork_parent(); + if (print_stats) + GC_log_printf("Forked child process (or failed)\n"); + } else { + GC_atfork_child(); + if (print_stats) + GC_log_printf("Started a child process\n"); +# ifdef THREADS +# ifdef PARALLEL_MARK + GC_gcollect(); /* no parallel markers */ +# endif + GC_start_mark_threads(); +# endif + GC_gcollect(); +# ifdef THREADS + tiny_reverse_test(0); + GC_gcollect(); +# endif + if (print_stats) + GC_log_printf("Finished a child process\n"); + exit(0); + } +# endif /* Repeated list reversal test. */ GET_TIME(start_time); reverse_test(); @@ -1335,16 +1374,6 @@ void run_one_test(void) /* GC_allocate_ml and GC_need_to_lock are no longer exported, and */ /* AO_fetch_and_add1() may be unavailable to update a counter. */ (void)GC_call_with_alloc_lock(inc_int_counter, &n_tests); -# ifndef NO_TEST_HANDLE_FORK - if (fork() == 0) { - GC_gcollect(); - tiny_reverse_test(0); - GC_gcollect(); - if (print_stats) - GC_log_printf("Finished a child process\n"); - exit(0); - } -# endif if (print_stats) GC_log_printf("Finished %p\n", (void *)&start_time); } @@ -647,7 +647,7 @@ GC_API void * GC_CALL GC_malloc_explicitly_typed_ignore_off_page(size_t lb, } else { op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); if (op != NULL) { - lg = BYTES_TO_WORDS(GC_size(op)); + lg = BYTES_TO_GRANULES(GC_size(op)); ((word *)op)[GRANULES_TO_WORDS(lg) - 1] = d; } } diff --git a/win32_threads.c b/win32_threads.c index 66a983ca..92b34f6f 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -60,7 +60,7 @@ STATIC void GC_thread_exit_proc(void *arg); # include <pthread.h> -# ifdef CAN_HANDLE_FORK +# ifdef CAN_CALL_ATFORK # include <unistd.h> # endif @@ -1048,7 +1048,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, # endif } - STATIC void GC_fork_prepare_proc(void) + static void fork_prepare_proc(void) { LOCK(); # ifdef PARALLEL_MARK @@ -1062,7 +1062,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, # endif } - STATIC void GC_fork_parent_proc(void) + static void fork_parent_proc(void) { # ifdef PARALLEL_MARK if (GC_parallel) @@ -1071,7 +1071,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, UNLOCK(); } - STATIC void GC_fork_child_proc(void) + static void fork_child_proc(void) { # ifdef PARALLEL_MARK if (GC_parallel) { @@ -1085,6 +1085,25 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, GC_remove_all_threads_but_me(); UNLOCK(); } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } #endif /* CAN_HANDLE_FORK */ void GC_push_thread_structures(void) @@ -1650,6 +1669,18 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # define GC_PTHREADS_PARAMARK # endif +# if !defined(GC_PTHREADS_PARAMARK) && defined(DONT_USE_SIGNALANDWAIT) + STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0}; + /* Events with manual reset (one for each */ + /* mark helper). */ + + STATIC DWORD GC_marker_Id[MAX_MARKERS - 1] = {0}; + /* This table is used for mapping helper */ + /* threads ID to mark helper index (linear */ + /* search is used since the mapping contains */ + /* only a few entries). */ +# endif + /* GC_mark_thread() is the same as in pthread_support.c */ # ifdef GC_PTHREADS_PARAMARK STATIC void * GC_mark_thread(void * id) @@ -1668,6 +1699,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # ifdef IA64 marker_bsp[(word)id] = GC_save_regs_in_stack(); # endif +# if !defined(GC_PTHREADS_PARAMARK) && defined(DONT_USE_SIGNALANDWAIT) + GC_marker_Id[(word)id] = GetCurrentThreadId(); +# endif for (;; ++my_mark_no) { if (my_mark_no - GC_mark_no > (word)2) { @@ -1689,6 +1723,10 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, /* GC_mark_threads[] is unused here unlike that in pthread_support.c */ +# ifndef CAN_HANDLE_FORK +# define available_markers_m1 GC_markers_m1 +# endif + # ifdef GC_PTHREADS_PARAMARK # include <pthread.h> @@ -1696,32 +1734,43 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # define NUMERIC_THREAD_ID(id) (unsigned long)GC_PTHREAD_PTRVAL(id) # endif - /* start_mark_threads() is the same as in pthread_support.c except for: */ - /* - GC_markers_m1 value is adjusted already; */ - /* - thread stack is assumed to be large enough; and */ - /* - statistics about the number of marker threads is printed outside. */ - static void start_mark_threads(void) + /* start_mark_threads is the same as in pthread_support.c except */ + /* for thread stack that is assumed to be large enough. */ +# ifdef CAN_HANDLE_FORK + static int available_markers_m1 = 0; +# define start_mark_threads GC_start_mark_threads + GC_API void GC_CALL +# else + static void +# endif + start_mark_threads(void) { int i; pthread_attr_t attr; pthread_t new_thread; - if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); + GC_ASSERT(I_DONT_HOLD_LOCK()); +# ifdef CAN_HANDLE_FORK + if (available_markers_m1 <= 0 || GC_parallel) return; + /* Skip if parallel markers disabled or already started. */ +# endif + if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) ABORT("pthread_attr_setdetachstate failed"); - for (i = 0; i < GC_markers_m1; ++i) { + for (i = 0; i < available_markers_m1; ++i) { marker_last_stack_min[i] = ADDR_LIMIT; if (0 != pthread_create(&new_thread, &attr, GC_mark_thread, (void *)(word)i)) { WARN("Marker thread creation failed.\n", 0); /* Don't try to create other marker threads. */ - GC_markers_m1 = i; break; } } + GC_markers_m1 = i; pthread_attr_destroy(&attr); + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); } static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -1824,18 +1873,6 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # else /* ! GC_PTHREADS_PARAMARK */ -# ifdef DONT_USE_SIGNALANDWAIT - STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1] = {0}; - /* Events with manual reset (one for each */ - /* mark helper). */ - - STATIC DWORD GC_marker_Id[MAX_MARKERS - 1] = {0}; - /* This table is used for mapping helper */ - /* threads ID to mark helper index (linear */ - /* search is used since the mapping contains */ - /* only a few entries). */ -# endif - # ifndef MARK_THREAD_STACK_SIZE # define MARK_THREAD_STACK_SIZE 0 /* default value */ # endif @@ -1857,10 +1894,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # endif # ifdef DONT_USE_SIGNALANDWAIT - /* Initialize GC_marker_cv[] and GC_marker_Id[] fully before */ - /* starting the first helper thread. */ + /* Initialize GC_marker_cv[] fully before starting the */ + /* first helper thread. */ for (i = 0; i < GC_markers_m1; ++i) { - GC_marker_Id[i] = GetCurrentThreadId(); if ((GC_marker_cv[i] = CreateEvent(NULL /* attrs */, TRUE /* isManualReset */, FALSE /* initialState */, @@ -1909,6 +1945,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # else GC_markers_m1 = i; # endif + GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); if (i == 0) { CloseHandle(mark_cv); CloseHandle(builder_cv); @@ -2190,7 +2227,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, GC_API HANDLE WINAPI GC_CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, - DWORD dwStackSize, + GC_WIN32_SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) @@ -2386,10 +2423,17 @@ GC_INNER void GC_thr_init(void) # ifdef CAN_HANDLE_FORK /* Prepare for forks if requested. */ - if (GC_handle_fork - && pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc, - GC_fork_child_proc) != 0) - ABORT("pthread_atfork failed"); + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } # endif /* Add the initial thread, so we can stop it. */ @@ -2400,20 +2444,21 @@ GC_INNER void GC_thr_init(void) GC_ASSERT(sb_result == GC_SUCCESS); # if defined(PARALLEL_MARK) - /* Set GC_markers_m1. */ { char * markers_string = GETENV("GC_MARKERS"); + int markers_m1; + if (markers_string != NULL) { - GC_markers_m1 = atoi(markers_string) - 1; - if (GC_markers_m1 >= MAX_MARKERS) { + markers_m1 = atoi(markers_string) - 1; + if (markers_m1 >= MAX_MARKERS) { WARN("Limiting number of mark threads\n", 0); - GC_markers_m1 = MAX_MARKERS - 1; + markers_m1 = MAX_MARKERS - 1; } } else { # ifdef MSWINCE /* There is no GetProcessAffinityMask() in WinCE. */ /* GC_sysinfo is already initialized. */ - GC_markers_m1 = (int)GC_sysinfo.dwNumberOfProcessors - 1; + markers_m1 = (int)GC_sysinfo.dwNumberOfProcessors - 1; # else # ifdef _WIN64 DWORD_PTR procMask = 0; @@ -2430,16 +2475,17 @@ GC_INNER void GC_thr_init(void) ncpu++; } while ((procMask &= procMask - 1) != 0); } - GC_markers_m1 = ncpu - 1; + markers_m1 = ncpu - 1; # endif # ifdef GC_MIN_MARKERS /* This is primarily for testing on systems without getenv(). */ - if (GC_markers_m1 < GC_MIN_MARKERS - 1) - GC_markers_m1 = GC_MIN_MARKERS - 1; + if (markers_m1 < GC_MIN_MARKERS - 1) + markers_m1 = GC_MIN_MARKERS - 1; # endif - if (GC_markers_m1 >= MAX_MARKERS) - GC_markers_m1 = MAX_MARKERS - 1; /* silently limit the value */ + if (markers_m1 >= MAX_MARKERS) + markers_m1 = MAX_MARKERS - 1; /* silently limit the value */ } + available_markers_m1 = markers_m1; } /* Check whether parallel mode could be enabled. */ @@ -2449,7 +2495,7 @@ GC_INNER void GC_thr_init(void) HMODULE hK32; /* SignalObjectAndWait() API call works only under NT. */ # endif - if (GC_win32_dll_threads || GC_markers_m1 <= 0 + if (GC_win32_dll_threads || available_markers_m1 <= 0 # if !defined(GC_PTHREADS_PARAMARK) && !defined(MSWINCE) \ && !defined(DONT_USE_SIGNALANDWAIT) || GC_wnt == FALSE @@ -2460,6 +2506,8 @@ GC_INNER void GC_thr_init(void) ) { /* Disable parallel marking. */ GC_parallel = FALSE; + GC_COND_LOG_PRINTF( + "Single marker thread, turning off parallel marking\n"); } else { # ifndef GC_PTHREADS_PARAMARK /* Initialize Win32 event objects for parallel marking. */ @@ -2485,9 +2533,13 @@ GC_INNER void GC_thr_init(void) GC_register_my_thread_inner(&sb, GC_main_thread); # ifdef PARALLEL_MARK - /* If we are using a parallel marker, actually start helper threads. */ - if (GC_parallel) start_mark_threads(); - GC_COND_LOG_PRINTF("Started %d mark helper threads\n", GC_markers_m1); +# ifndef CAN_HANDLE_FORK + if (GC_parallel) +# endif + { + /* If we are using a parallel marker, actually start helper threads. */ + start_mark_threads(); + } # endif } |