From 86991cba3cbce83bfd3a0619f11f86cc1ee56917 Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Fri, 28 Aug 2015 01:37:33 +0300 Subject: [PATCH] Code refactoring of toggle-ref support * alloc.c (GC_stopped_mark): Move GC_process_togglerefs() call from GC_stop_world; do not call it if GC_NO_FINALIZATION or GC_TOGGLE_REFS_NOT_NEEDED. * darwin_stop_world.c (GC_stop_world): Remove GC_process_togglerefs() call. * pthread_stop_world.c (GC_stop_world): Likewise. * doc/README.macros (GC_TOGGLE_REFS_NOT_NEEDED): Document. * finalize.c (GCToggleRef, GC_process_togglerefs, push_and_mark_object, GC_clear_togglerefs, GC_toggleref_add): Replace GC_PTR with void*. * include/gc.h (GC_toggleref_add): Likewise. * finalize.c (GCToggleRef, GC_toggleref_callback, GC_toggleref_arr, GC_toggleref_array_size, GC_toggleref_array_capacity, GC_process_togglerefs, push_and_mark_object, GC_mark_togglerefs, GC_clear_togglerefs, GC_set_toggleref_func, ensure_toggleref_capacity, GC_toggleref_add): Do not defined if GC_TOGGLE_REFS_NOT_NEEDED. * finalize.c (GCToggleRef): Add comment. * finalize.c (GC_toggleref_array): Rename to GC_toggleref_arr. * finalize.c (GC_toggleref_callback, GC_toggleref_array, GC_toggleref_array_size, GC_toggleref_array_capacity): Make it STATIC (instead "static"). * finalize.c (GC_process_togglerefs): Decorate with GC_INNER; remove "toggle_ref_counts", "res" local variables; rename "w" local variable to "new_size"; add assertion on lock state; use GC_TOGGLE_REF_* enum element in switch statement; use BZERO to clear moved elements of GC_toggleref_arr. * finalize.c (GC_normal_finalize_mark_proc): Declare (before use). * finalize.c (push_and_mark_object): Replace PUSH_OBJ with GC_normal_finalize_mark_proc call. * finalize.c (GC_mark_togglerefs, GC_clear_togglerefs): Remove "object" local variable. * finalize.c (GC_toggleref_register_callback): Rename to GC_set_toggleref_func; change argument to GC_toggleref_func (which returns GC_ToggleRefStatus instead of int). * finalize.c (GC_toggleref_register_callback, GC_toggleref_add): Decorate with GC_API and GC_CALL. * include/gc.h (GC_toggleref_register_callback): Likewise. * finalize.c (GC_set_toggleref_func): Acquire allocation lock. * finalize.c (GC_get_toggleref_func): New API function. * finalize.c (ensure_toggleref_capacity): Rename "capacity" argument to "capacity_inc"; add assertion on argument value; rename "tmp" local variable to "new_array"; remove unused "old_capacity" variable; replace memcpy() with BCOPY() call. * finalize.c (GC_toggleref_add): Rename "strong_ref" argument to "is_strong_ref". * finalize.c (GC_finalize): Do not call GC_clear_togglerefs and GC_mark_togglerefs if GC_TOGGLE_REFS_NOT_NEEDED. * include/gc.h (GC_ToggleRefStatus, GC_toggleref_func): New type. * include/gc.h (GC_toggleref_register_callback): Add comment (including about locking). * include/gc.h (GC_get_toggleref_func): New API function declaration. * include/gc.h (GC_toggleref_add): Decorate with GC_CALL; add comment; add GC_ATTR_NONNULL attribute. * include/private/gc_priv.h (GC_process_togglerefs): Do not declare if GC_TOGGLE_REFS_NOT_NEEDED; decorate with GC_INNER. --- alloc.c | 5 +- darwin_stop_world.c | 1 - doc/README.macros | 4 +- finalize.c | 283 ++++++++++++++++++++------------------ include/gc.h | 40 +++++- include/private/gc_priv.h | 21 +-- pthread_stop_world.c | 1 - 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 -- 2.40.0