diff options
author | Rodrigo Kumpera <kumpera@gmail.com> | 2014-02-26 12:14:39 -0500 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2015-08-26 23:19:39 +0300 |
commit | 5ae0aed4befd61980cb9930a982e37c051be1d5d (patch) | |
tree | f232843d4e9994ff04324b31b5a445d64e9ac783 /finalize.c | |
parent | 8320da593dcc8fa9e99e63bc9c6d4857fc4367ae (diff) | |
download | bdwgc-5ae0aed4befd61980cb9930a982e37c051be1d5d.tar.gz |
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.
Diffstat (limited to 'finalize.c')
-rw-r--r-- | finalize.c | 154 |
1 files changed, 154 insertions, 0 deletions
@@ -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); |