From 5ae0aed4befd61980cb9930a982e37c051be1d5d Mon Sep 17 00:00:00 2001 From: Rodrigo Kumpera Date: Wed, 26 Feb 2014 12:14:39 -0500 Subject: [PATCH] Add toggle-ref support (Apply commit af75406 from 'mono_libgc' branch.) GC backed toggleref machinery. This enables an embedder to support toggleref style of external memory management without hooking up to the host retain/release machinery. The API export two entry points. The first let the caller register a callback that decides the state of a given object, by probably inspecting its native state. The second allows registration of objects with the toggleref machinery. The idea of toggleref is that we keep an external reference to an object and it can be either a strong or weak reference. We use a weak reference when the external peer has no interest into the object and a strong otherwise. --- darwin_stop_world.c | 1 + finalize.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ include/gc.h | 5 ++ include/private/gc_priv.h | 3 + pthread_stop_world.c | 1 + 5 files changed, 164 insertions(+) diff --git a/darwin_stop_world.c b/darwin_stop_world.c index 21e7e1c..90aa909 100644 --- a/darwin_stop_world.c +++ b/darwin_stop_world.c @@ -534,6 +534,7 @@ 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/finalize.c b/finalize.c index dc5022b..f58d220 100644 --- a/finalize.c +++ b/finalize.c @@ -260,6 +260,158 @@ GC_API int GC_CALL GC_unregister_disappearing_link(void * * link) return 1; } +/* toggleref support */ +typedef struct { + GC_PTR strong_ref; + GC_hidden_pointer weak_ref; +} 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; + + +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"); + } + } + + for (i = w; i < GC_toggleref_array_size; ++i) { + GC_toggleref_array [w].strong_ref = NULL; + GC_toggleref_array [w].weak_ref = (GC_hidden_pointer)NULL; + } + + GC_toggleref_array_size = w; +} + + +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 GC_mark_togglerefs () +{ + int i; + if (!GC_toggleref_array) + return; + + GC_set_mark_bit (GC_toggleref_array); + 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); + } + } +} + +static void GC_clear_togglerefs () +{ + 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 */ + } + } + } +} + + + +void GC_toggleref_register_callback(int (*proccess_toggleref) (GC_PTR obj)) +{ + GC_toggleref_callback = proccess_toggleref; +} + +static void +ensure_toggleref_capacity (int capacity) +{ + if (!GC_toggleref_array) { + GC_toggleref_array_capacity = 32; + GC_toggleref_array = (GCToggleRef *) GC_INTERNAL_MALLOC (GC_toggleref_array_capacity * sizeof (GCToggleRef), NORMAL); + } + if (GC_toggleref_array_size + capacity >= GC_toggleref_array_capacity) { + GCToggleRef *tmp; + int old_capacity = GC_toggleref_array_capacity; + while (GC_toggleref_array_capacity < GC_toggleref_array_size + capacity) + GC_toggleref_array_capacity *= 2; + + tmp = (GCToggleRef *) GC_INTERNAL_MALLOC (GC_toggleref_array_capacity * sizeof (GCToggleRef), NORMAL); + memcpy (tmp, GC_toggleref_array, GC_toggleref_array_size * sizeof (GCToggleRef)); + + GC_free((GC_PTR)GC_toggleref_array); + GC_toggleref_array = tmp; + } +} + +void +GC_toggleref_add (GC_PTR object, int strong_ref) +{ + DCL_LOCK_STATE; + LOCK(); + + if (!GC_toggleref_callback) + goto end; + + ensure_toggleref_capacity (1); + 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(); +} + + /* Finalizer callback support. */ STATIC GC_await_finalize_proc GC_object_finalized_proc = 0; @@ -770,6 +922,7 @@ GC_INNER void GC_finalize(void) # endif # endif + GC_mark_togglerefs(); GC_make_disappearing_links_disappear(&GC_dl_hashtbl); /* Mark all objects reachable via chains of 1 or more pointers */ @@ -883,6 +1036,7 @@ GC_INNER void GC_finalize(void) } GC_remove_dangling_disappearing_links(&GC_dl_hashtbl); + GC_clear_togglerefs (); # 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 d142381..04cf374 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1172,6 +1172,11 @@ 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 void GC_toggleref_add (GC_PTR object, int strong_ref); + /* Finalizer callback support. Invoked by the collector (with */ /* the allocation lock held) for each unreachable object */ /* enqueued for finalization. */ diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index bb652c6..14b02ad 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -270,6 +270,9 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /* 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 diff --git a/pthread_stop_world.c b/pthread_stop_world.c index c6d4915..89cfc30 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -605,6 +605,7 @@ 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.7.4