summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2012-12-09 22:41:54 +0400
committerIvan Maidanski <ivmai@mail.ru>2012-12-09 22:41:54 +0400
commit6e5f9a6eabc9c9f09770b51920d09827a23a4309 (patch)
treebd8ab24c0a6adb7fae3ef634ec396bb57e10be6c
parent3939770d7751909cdef53c561b5604621e71fd0f (diff)
parent72092ed8dee8c64ee5b92a06a7628fae085d0cf2 (diff)
downloadbdwgc-add-long-weakref.tar.gz
Merge branch 'master' into add-long-weakrefadd-long-weakref
Conflicts: finalize.c
-rw-r--r--AUTHORS3
-rw-r--r--TODO8
-rw-r--r--alloc.c19
-rw-r--r--doc/README.macros2
-rw-r--r--finalize.c20
-rw-r--r--include/gc.h48
-rw-r--r--include/gc_mark.h16
-rw-r--r--include/private/gc_priv.h86
-rw-r--r--include/private/gcconfig.h18
-rw-r--r--mallocx.c5
-rw-r--r--misc.c182
-rw-r--r--pthread_stop_world.c8
-rw-r--r--pthread_support.c106
-rw-r--r--reclaim.c4
-rw-r--r--tests/test.c57
-rw-r--r--typd_mlc.c2
-rw-r--r--win32_threads.c144
17 files changed, 525 insertions, 203 deletions
diff --git a/AUTHORS b/AUTHORS
index 02334c9f..f9d6f182 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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>
diff --git a/TODO b/TODO
index d6499d61..8c9c7d2f 100644
--- a/TODO
+++ b/TODO
@@ -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*)".
diff --git a/alloc.c b/alloc.c
index c0440e11..925c53bb 100644
--- a/alloc.c
+++ b/alloc.c
@@ -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
diff --git a/finalize.c b/finalize.c
index 63696699..f50cb261 100644
--- a/finalize.c
+++ b/finalize.c
@@ -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. */
diff --git a/mallocx.c b/mallocx.c
index bf7f9f06..db265103 100644
--- a/mallocx.c
+++ b/mallocx.c
@@ -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;
diff --git a/misc.c b/misc.c
index fb56bf39..7d3a1e9f 100644
--- a/misc.c
+++ b/misc.c
@@ -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
}
diff --git a/reclaim.c b/reclaim.c
index 1cf75550..285e1460 100644
--- a/reclaim.c
+++ b/reclaim.c
@@ -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);
}
diff --git a/typd_mlc.c b/typd_mlc.c
index e1d3b2ab..ef191e52 100644
--- a/typd_mlc.c
+++ b/typd_mlc.c
@@ -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
}