Code refactoring of toggle-ref support
authorIvan Maidanski <ivmai@mail.ru>
Thu, 27 Aug 2015 22:37:33 +0000 (01:37 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Fri, 28 Aug 2015 08:20:47 +0000 (11:20 +0300)
* 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
darwin_stop_world.c
doc/README.macros
finalize.c
include/gc.h
include/private/gc_priv.h
pthread_stop_world.c

diff --git a/alloc.c b/alloc.c
index 9eb1c06..7194802 100644 (file)
--- 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);
index 90aa909..21e7e1c 100644 (file)
@@ -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
index 63a96f8..f5601ae 100644 (file)
@@ -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,
index 90d5f42..986d5e5 100644 (file)
@@ -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);
index 5a407cb..83f724c 100644 (file)
@@ -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        */
index 14b02ad..ed9fc56 100644 (file)
@@ -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)
index 89cfc30..c6d4915 100644 (file)
@@ -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