summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alloc.c5
-rw-r--r--darwin_stop_world.c1
-rw-r--r--doc/README.macros4
-rw-r--r--finalize.c283
-rw-r--r--include/gc.h40
-rw-r--r--include/private/gc_priv.h21
-rw-r--r--pthread_stop_world.c1
7 files changed, 202 insertions, 153 deletions
diff --git a/alloc.c b/alloc.c
index 9eb1c06e..7194802a 100644
--- a/alloc.c
+++ b/alloc.c
@@ -637,13 +637,14 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func)
GET_TIME(start_time);
# endif
+# if !defined(GC_NO_FINALIZATION) && !defined(GC_TOGGLE_REFS_NOT_NEEDED)
+ GC_process_togglerefs();
+# endif
# ifdef THREADS
if (GC_on_collection_event)
GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD);
# endif
-
STOP_WORLD();
-
# ifdef THREADS
if (GC_on_collection_event)
GC_on_collection_event(GC_EVENT_POST_STOP_WORLD);
diff --git a/darwin_stop_world.c b/darwin_stop_world.c
index 90aa9092..21e7e1ce 100644
--- a/darwin_stop_world.c
+++ b/darwin_stop_world.c
@@ -534,7 +534,6 @@ GC_INNER void GC_stop_world(void)
mach_port_t my_thread = mach_thread_self();
kern_return_t kern_result;
- GC_process_togglerefs ();
# ifdef DEBUG_THREADS
GC_log_printf("Stopping the world from thread %p\n", (void *)my_thread);
# endif
diff --git a/doc/README.macros b/doc/README.macros
index 63a96f80..f5601ae4 100644
--- a/doc/README.macros
+++ b/doc/README.macros
@@ -261,7 +261,9 @@ FINALIZE_ON_DEMAND Causes finalizers to be run only in response
In 5.0 this became runtime adjustable, and this only determines the
initial value of GC_finalize_on_demand.
-GC_NO_FINALIZATION Exclude finalization support (for smaller code size)
+GC_NO_FINALIZATION Exclude finalization support (for smaller code size).
+
+GC_TOGGLE_REFS_NOT_NEEDED Exclude toggle-refs support.
GC_ATOMIC_UNCOLLECTABLE Includes code for GC_malloc_atomic_uncollectable.
This is useful if either the vendor malloc implementation is poor,
diff --git a/finalize.c b/finalize.c
index 90d5f424..986d5e51 100644
--- a/finalize.c
+++ b/finalize.c
@@ -260,171 +260,182 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
return 1;
}
-/* toggleref support */
-typedef struct {
- GC_PTR strong_ref;
+/* Toggle-ref support. */
+#ifndef GC_TOGGLE_REFS_NOT_NEEDED
+ typedef struct {
+ /* Only one of the two fields can be non-NULL at a time. */
+ void *strong_ref;
GC_hidden_pointer weak_ref;
-} GCToggleRef;
+ } GCToggleRef;
-static int (*GC_toggleref_callback) (GC_PTR obj);
-static GCToggleRef *GC_toggleref_array;
-static int GC_toggleref_array_size;
-static int GC_toggleref_array_capacity;
+ STATIC GC_toggleref_func GC_toggleref_callback = 0;
+ STATIC GCToggleRef *GC_toggleref_arr = NULL;
+ STATIC int GC_toggleref_array_size = 0;
+ STATIC int GC_toggleref_array_capacity = 0;
+ GC_INNER void GC_process_togglerefs(void)
+ {
+ int i;
+ int new_size = 0;
-void
-GC_process_togglerefs (void)
-{
- int i, w;
- int toggle_ref_counts [3] = { 0, 0, 0 };
-
- for (i = w = 0; i < GC_toggleref_array_size; ++i) {
- int res;
- GCToggleRef r = GC_toggleref_array [i];
-
- GC_PTR obj;
-
- if (r.strong_ref)
- obj = r.strong_ref;
- else if (r.weak_ref)
- obj = GC_REVEAL_POINTER (r.weak_ref);
- else
- continue;
-
- res = GC_toggleref_callback (obj);
- ++toggle_ref_counts [res];
- switch (res) {
- case 0:
- break;
- case 1:
- GC_toggleref_array [w].strong_ref = obj;
- GC_toggleref_array [w].weak_ref = (GC_hidden_pointer)NULL;
- ++w;
- break;
- case 2:
- GC_toggleref_array [w].strong_ref = NULL;
- GC_toggleref_array [w].weak_ref = GC_HIDE_POINTER (obj);
- ++w;
- break;
- default:
- ABORT("Invalid callback result");
+ GC_ASSERT(I_HOLD_LOCK());
+ for (i = 0; i < GC_toggleref_array_size; ++i) {
+ GCToggleRef r = GC_toggleref_arr[i];
+ void *obj = r.strong_ref;
+
+ if (NULL == obj) {
+ if (r.weak_ref != 0) {
+ obj = GC_REVEAL_POINTER(r.weak_ref);
+ } else {
+ continue;
}
+ }
+ switch (GC_toggleref_callback(obj)) {
+ case GC_TOGGLE_REF_DROP:
+ break;
+ case GC_TOGGLE_REF_STRONG:
+ GC_toggleref_arr[new_size].strong_ref = obj;
+ GC_toggleref_arr[new_size].weak_ref = 0;
+ ++new_size;
+ break;
+ case GC_TOGGLE_REF_WEAK:
+ GC_toggleref_arr[new_size].strong_ref = NULL;
+ GC_toggleref_arr[new_size].weak_ref = GC_HIDE_POINTER(obj);
+ ++new_size;
+ break;
+ default:
+ ABORT("Bad toggle-ref status returned by callback");
+ }
}
- for (i = w; i < GC_toggleref_array_size; ++i) {
- GC_toggleref_array [i].strong_ref = NULL;
- GC_toggleref_array [i].weak_ref = (GC_hidden_pointer)NULL;
+ if (new_size < GC_toggleref_array_size) {
+ BZERO(&GC_toggleref_arr[new_size],
+ (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef));
+ GC_toggleref_array_size = new_size;
}
+ }
- GC_toggleref_array_size = w;
-}
-
+ STATIC void GC_normal_finalize_mark_proc(ptr_t);
-static void push_and_mark_object (GC_PTR p)
-{
- hdr * hhdr = HDR(p);
-
- PUSH_OBJ(p, hhdr, GC_mark_stack_top,
- &(GC_mark_stack[GC_mark_stack_size]));
-
- while (!GC_mark_stack_empty()) MARK_FROM_MARK_STACK();
- GC_set_mark_bit (p);
- if (GC_mark_state != MS_NONE)
- while (!GC_mark_some((ptr_t)0)) {}
-}
+ static void push_and_mark_object(void *p)
+ {
+ GC_normal_finalize_mark_proc(p);
+ while (!GC_mark_stack_empty()) {
+ MARK_FROM_MARK_STACK();
+ }
+ GC_set_mark_bit(p);
+ if (GC_mark_state != MS_NONE) {
+ while (!GC_mark_some(0)) {
+ /* Empty. */
+ }
+ }
+ }
-static void GC_mark_togglerefs ()
-{
+ STATIC void GC_mark_togglerefs(void)
+ {
int i;
- if (!GC_toggleref_array)
- return;
+ if (NULL == GC_toggleref_arr)
+ return;
- GC_set_mark_bit (GC_toggleref_array);
+ /* TODO: Hide GC_toggleref_arr to avoid its marking from roots. */
+ GC_set_mark_bit(GC_toggleref_arr);
for (i = 0; i < GC_toggleref_array_size; ++i) {
- if (GC_toggleref_array [i].strong_ref) {
- GC_PTR object = GC_toggleref_array [i].strong_ref;
-
- push_and_mark_object (object);
- }
+ if (GC_toggleref_arr[i].strong_ref != NULL) {
+ push_and_mark_object(GC_toggleref_arr[i].strong_ref);
+ }
}
-}
+ }
-static void GC_clear_togglerefs ()
-{
+ STATIC void GC_clear_togglerefs(void)
+ {
int i;
for (i = 0; i < GC_toggleref_array_size; ++i) {
- if (GC_toggleref_array [i].weak_ref) {
- GC_PTR object = GC_REVEAL_POINTER (GC_toggleref_array [i].weak_ref);
-
- if (!GC_is_marked (object)) {
- GC_toggleref_array [i].weak_ref = (GC_hidden_pointer)NULL; /* We defer compaction to only happen on the callback step. */
- } else {
- /*No need to copy, boehm is non-moving */
- }
+ if (GC_toggleref_arr[i].weak_ref != 0) {
+ if (!GC_is_marked(GC_REVEAL_POINTER(GC_toggleref_arr[i].weak_ref))) {
+ GC_toggleref_arr[i].weak_ref = 0;
+ } else {
+ /* No need to copy, BDWGC is a non-moving collector. */
}
+ }
}
-}
+ }
+ GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn)
+ {
+ DCL_LOCK_STATE;
+ LOCK();
+ GC_toggleref_callback = fn;
+ UNLOCK();
+ }
-void GC_toggleref_register_callback(int (*proccess_toggleref) (GC_PTR obj))
-{
- GC_toggleref_callback = proccess_toggleref;
-}
+ GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void)
+ {
+ GC_toggleref_func fn;
+ DCL_LOCK_STATE;
-static GC_bool
-ensure_toggleref_capacity (int capacity)
-{
- if (!GC_toggleref_array) {
- GC_toggleref_array_capacity = 32;
- GC_toggleref_array = (GCToggleRef *) GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE (GC_toggleref_array_capacity * sizeof (GCToggleRef), NORMAL);
- if (NULL == GC_toggleref_array)
- return FALSE;
- }
- if ((unsigned)GC_toggleref_array_size + (unsigned)capacity
- >= (unsigned)GC_toggleref_array_capacity) {
- GCToggleRef *tmp;
- int old_capacity = GC_toggleref_array_capacity;
- while ((unsigned)GC_toggleref_array_capacity
- < (unsigned)GC_toggleref_array_size + (unsigned)capacity) {
- GC_toggleref_array_capacity *= 2;
- if (GC_toggleref_array_capacity < 0) /* overflow */
- return FALSE;
- }
+ LOCK();
+ fn = GC_toggleref_callback;
+ UNLOCK();
+ return fn;
+ }
- tmp = (GCToggleRef *) GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE (GC_toggleref_array_capacity * sizeof (GCToggleRef), NORMAL);
- if (NULL == tmp)
- return FALSE;
- memcpy (tmp, GC_toggleref_array, GC_toggleref_array_size * sizeof (GCToggleRef));
+ static GC_bool ensure_toggleref_capacity(int capacity_inc)
+ {
+ GC_ASSERT(capacity_inc >= 0);
+ if (NULL == GC_toggleref_arr) {
+ GC_toggleref_array_capacity = 32; /* initial capacity */
+ GC_toggleref_arr = GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
+ GC_toggleref_array_capacity * sizeof(GCToggleRef),
+ NORMAL);
+ if (NULL == GC_toggleref_arr)
+ return FALSE;
+ }
+ if ((unsigned)GC_toggleref_array_size + (unsigned)capacity_inc
+ >= (unsigned)GC_toggleref_array_capacity) {
+ GCToggleRef *new_array;
+ while ((unsigned)GC_toggleref_array_capacity
+ < (unsigned)GC_toggleref_array_size + (unsigned)capacity_inc) {
+ GC_toggleref_array_capacity *= 2;
+ if (GC_toggleref_array_capacity < 0) /* overflow */
+ return FALSE;
+ }
- GC_INTERNAL_FREE(GC_toggleref_array);
- GC_toggleref_array = tmp;
+ new_array = GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
+ GC_toggleref_array_capacity * sizeof(GCToggleRef),
+ NORMAL);
+ if (NULL == new_array)
+ return FALSE;
+ BCOPY(GC_toggleref_arr, new_array,
+ GC_toggleref_array_size * sizeof(GCToggleRef));
+ GC_INTERNAL_FREE(GC_toggleref_arr);
+ GC_toggleref_arr = new_array;
}
return TRUE;
-}
+ }
-int
-GC_toggleref_add (GC_PTR object, int strong_ref)
-{
+ GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref)
+ {
+ int res = GC_SUCCESS;
DCL_LOCK_STATE;
- LOCK();
- if (!GC_toggleref_callback)
- goto end;
-
- if (!ensure_toggleref_capacity(1)) {
- UNLOCK();
- return GC_NO_MEMORY;
+ GC_ASSERT(obj != NULL);
+ LOCK();
+ if (GC_toggleref_callback != 0) {
+ if (!ensure_toggleref_capacity(1)) {
+ res = GC_NO_MEMORY;
+ } else {
+ GC_toggleref_arr[GC_toggleref_array_size].strong_ref =
+ is_strong_ref ? obj : NULL;
+ GC_toggleref_arr[GC_toggleref_array_size].weak_ref =
+ is_strong_ref ? 0 : GC_HIDE_POINTER(obj);
+ ++GC_toggleref_array_size;
+ }
}
- GC_toggleref_array [GC_toggleref_array_size].strong_ref = strong_ref ? object : NULL;
- GC_toggleref_array [GC_toggleref_array_size].weak_ref = strong_ref ? (GC_hidden_pointer)NULL : GC_HIDE_POINTER (object);
- ++GC_toggleref_array_size;
-
-end:
UNLOCK();
- return GC_SUCCESS;
-}
-
+ return res;
+ }
+#endif /* !GC_TOGGLE_REFS_NOT_NEEDED */
/* Finalizer callback support. */
STATIC GC_await_finalize_proc GC_object_finalized_proc = 0;
@@ -936,7 +947,9 @@ GC_INNER void GC_finalize(void)
# endif
# endif
- GC_mark_togglerefs();
+# ifndef GC_TOGGLE_REFS_NOT_NEEDED
+ GC_mark_togglerefs();
+# endif
GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
/* Mark all objects reachable via chains of 1 or more pointers */
@@ -1050,7 +1063,9 @@ GC_INNER void GC_finalize(void)
}
GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
- GC_clear_togglerefs ();
+# ifndef GC_TOGGLE_REFS_NOT_NEEDED
+ GC_clear_togglerefs();
+# endif
# ifndef GC_LONG_REFS_NOT_NEEDED
GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
diff --git a/include/gc.h b/include/gc.h
index 5a407cba..83f724c8 100644
--- a/include/gc.h
+++ b/include/gc.h
@@ -1172,10 +1172,42 @@ GC_API int GC_CALL GC_unregister_long_link(void ** /* link */);
/* Similar to GC_unregister_disappearing_link but for a */
/* registration by either of the above two routines. */
-
-/* toggleref support */
-GC_API void GC_toggleref_register_callback (int (*proccess_toggleref) (GC_PTR obj));
-GC_API int GC_toggleref_add (GC_PTR object, int strong_ref);
+/* Support of toggle-ref style of external memory management */
+/* without hooking up to the host retain/release machinery. */
+/* The idea of toggle-ref is that an external reference to */
+/* an object is kept and it can be either a strong or weak */
+/* reference; a weak reference is used when the external peer */
+/* has no interest in the object, and a strong otherwise. */
+typedef enum {
+ GC_TOGGLE_REF_DROP,
+ GC_TOGGLE_REF_STRONG,
+ GC_TOGGLE_REF_WEAK
+} GC_ToggleRefStatus;
+
+/* The callback is to decide (return) the new state of a given */
+/* object. Invoked by the collector for all objects registered */
+/* for toggle-ref processing. Invoked with the allocation lock */
+/* held (but the "world" is running). */
+typedef GC_ToggleRefStatus (GC_CALLBACK *GC_toggleref_func)(void * /* obj */);
+
+/* Set (register) a callback that decides the state of a given */
+/* object (by, probably, inspecting its native state). */
+/* The argument may be 0 (means no callback). Both the setter */
+/* and the getter acquire the allocation lock (to avoid data */
+/* races). */
+GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func);
+GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void);
+
+/* Register a given object for toggle-ref processing. It will */
+/* be stored internally and the toggle-ref callback will be */
+/* invoked on the object until the callback returns */
+/* GC_TOGGLE_REF_DROP or the object is collected. If is_strong */
+/* is true then the object is registered with a strong ref, */
+/* a weak one otherwise. Returns GC_SUCCESS if registration */
+/* succeeded (or no callback registered yet), GC_NO_MEMORY if */
+/* it failed for lack of memory. */
+GC_API int GC_CALL GC_toggleref_add(void * /* obj */, int /* is_strong */)
+ GC_ATTR_NONNULL(1);
/* Finalizer callback support. Invoked by the collector (with */
/* the allocation lock held) for each unreachable object */
diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index 14b02ad7..ed9fc566 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -255,29 +255,30 @@ typedef char * ptr_t; /* A generic pointer to which we can add */
#ifndef GC_NO_FINALIZATION
-# define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers()
- GC_INNER void GC_notify_or_invoke_finalizers(void);
+# define GC_INVOKE_FINALIZERS() GC_notify_or_invoke_finalizers()
+ GC_INNER void GC_notify_or_invoke_finalizers(void);
/* If GC_finalize_on_demand is not set, invoke */
/* eligible finalizers. Otherwise: */
/* Call *GC_finalizer_notifier if there are */
/* finalizers to be run, and we haven't called */
/* this procedure yet this GC cycle. */
- GC_INNER void GC_finalize(void);
+ GC_INNER void GC_finalize(void);
/* Perform all indicated finalization actions */
/* on unmarked objects. */
/* Unreachable finalizable objects are enqueued */
/* for processing by GC_invoke_finalizers. */
/* Invoked with lock. */
- void GC_process_togglerefs (void);
- /*Process the togglerefs before GC starts */
-
-# ifndef SMALL_CONFIG
- GC_INNER void GC_print_finalization_stats(void);
-# endif
+# ifndef GC_TOGGLE_REFS_NOT_NEEDED
+ GC_INNER void GC_process_togglerefs(void);
+ /* Process the toggle-refs before GC starts. */
+# endif
+# ifndef SMALL_CONFIG
+ GC_INNER void GC_print_finalization_stats(void);
+# endif
#else
-# define GC_INVOKE_FINALIZERS() (void)0
+# define GC_INVOKE_FINALIZERS() (void)0
#endif /* GC_NO_FINALIZATION */
#if !defined(DONT_ADD_BYTE_AT_END)
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 89cfc305..c6d4915b 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -605,7 +605,6 @@ GC_INNER void GC_stop_world(void)
int code;
# endif
GC_ASSERT(I_HOLD_LOCK());
- GC_process_togglerefs ();
# ifdef DEBUG_THREADS
GC_log_printf("Stopping the world from %p\n", (void *)pthread_self());
# endif