From: Ivan Maidanski Date: Tue, 4 Aug 2015 22:20:32 +0000 (+0300) Subject: Make heap walker accept callback X-Git-Tag: gc7_6_0~161 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=84fcf4c60351ce8dc7251f2af8c08e0a25eb8ac0;p=gc Make heap walker accept callback * alloc.c (GC_mercury_callback_reachable_object): Remove. * include/gc.h (GC_mercury_callback_reachable_object): Likewise. * include/private/gc_priv.h (GC_mercury_enumerate_reachable_objects): Likewise. * alloc.c (GC_finish_collection): Do not call GC_mercury_enumerate_reachable_objects. * include/gc_mark.h (GC_reachable_object_proc): New public typedef. * include/gc_mark.h (GC_enumerate_reachable_objects_inner): New API function declaration. * reclaim.c (enumerate_reachable_s): New struct type. * reclaim.c (GC_mercury_do_enumerate_reachable_objects): Rename to GC_do_enumerate_reachable_objects; replace while() with for(); use 2nd argument to pass client callback and custom data; call client callback (passed via the argument) instead of GC_mercury_callback_reachable_object; pass object size in bytes instead of words to client callback. * reclaim.c (GC_mercury_enumerate_reachable_objects): Rename to GC_enumerate_reachable_objects_inner; decorate with GC_API/GC_CALL; add 2 arguments (client callback and custom data); remove assertion for GC_mercury_callback_reachable_object; add assertion for acquired lock. * tests/test.c: Include gc_mark.h unconditionally. * tests/test.c (reachable_objs_counter, reachable_objs_count_enumerator): New function. * tests/test.c (check_heap_stats): New local variable "obj_count"; invoke GC_call_with_alloc_lock(reachable_objs_count_enumerator); print final number of reachable objects. --- diff --git a/alloc.c b/alloc.c index 0a77fe53..9eb1c06e 100644 --- a/alloc.c +++ b/alloc.c @@ -63,10 +63,6 @@ word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ word GC_gc_no = 0; -void (*GC_mercury_callback_reachable_object)(GC_word *, size_t) = NULL; - /* Callbacks for mercury to notify */ - /* the runtime of certain events */ - #ifndef GC_DISABLE_INCREMENTAL GC_INNER int GC_incremental = 0; /* By default, stop the world. */ #endif @@ -934,9 +930,6 @@ STATIC void GC_finish_collection(void) } # endif COND_DUMP; - if (GC_mercury_callback_reachable_object) { - GC_mercury_enumerate_reachable_objects(); - } if (GC_find_leak) { /* Mark all objects on the free list. All objects should be */ /* marked when we're done. */ diff --git a/include/gc.h b/include/gc.h index e98431ca..d1423818 100644 --- a/include/gc.h +++ b/include/gc.h @@ -392,9 +392,6 @@ GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit; GC_API void GC_CALL GC_set_time_limit(unsigned long); GC_API unsigned long GC_CALL GC_get_time_limit(void); -GC_API void (*GC_mercury_callback_reachable_object)(GC_word *, size_t); - /* This object on the heap is reachable. */ - /* Public procedures */ /* Set whether the GC will allocate executable memory pages or not. */ diff --git a/include/gc_mark.h b/include/gc_mark.h index 28d2005a..7edb7f5e 100644 --- a/include/gc_mark.h +++ b/include/gc_mark.h @@ -289,6 +289,16 @@ typedef void (GC_CALLBACK * GC_push_other_roots_proc)(void); GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc); GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void); +/* Walk the GC heap visiting all reachable objects. Assume the caller */ +/* holds the allocation lock. Object base pointer, object size and */ +/* client custom data are passed to the callback (holding the lock). */ +typedef void (GC_CALLBACK *GC_reachable_object_proc)(void * /* obj */, + size_t /* bytes */, + void * /* client_data */); +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc, + void * /* client_data */) GC_ATTR_NONNULL(1); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index e369a7cc..f3f0718e 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -2082,8 +2082,6 @@ void GC_print_static_roots(void); extern word GC_fo_entries; /* should be visible in extra/MacOS.c */ -GC_INNER void GC_mercury_enumerate_reachable_objects(void); - #ifdef KEEP_BACK_PTRS GC_INNER void GC_store_back_pointer(ptr_t source, ptr_t dest); GC_INNER void GC_marked_for_finalization(ptr_t dest); diff --git a/reclaim.c b/reclaim.c index 0685f937..4f40cbb4 100644 --- a/reclaim.c +++ b/reclaim.c @@ -773,38 +773,45 @@ GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) } #endif /* !EAGER_SWEEP && ENABLE_DISCLAIM */ -STATIC void GC_mercury_do_enumerate_reachable_objects(struct hblk *hbp, - word dummy) +struct enumerate_reachable_s { + GC_reachable_object_proc proc; + void *client_data; +}; + +STATIC void GC_do_enumerate_reachable_objects(struct hblk *hbp, word ped) { - struct hblkhdr * hhdr = HDR(hbp); - size_t sz = hhdr -> hb_sz; - size_t bit_no; - char *p, *plim; + struct hblkhdr *hhdr = HDR(hbp); + size_t sz = hhdr -> hb_sz; + size_t bit_no; + char *p, *plim; - if (GC_block_empty(hhdr)) { - return; - } + if (GC_block_empty(hhdr)) { + return; + } - p = hbp->hb_body; - bit_no = 0; - if (sz > MAXOBJBYTES) { /* one big object */ - plim = p; - } else { - plim = hbp->hb_body + HBLKSIZE - sz; - } - /* Go through all words in block. */ - while (p <= plim) { - if (mark_bit_from_hdr(hhdr, bit_no)) { - GC_mercury_callback_reachable_object((GC_word *)p, - BYTES_TO_WORDS(sz)); - } - bit_no += MARK_BIT_OFFSET(sz); - p += sz; + p = hbp->hb_body; + if (sz > MAXOBJBYTES) { /* one big object */ + plim = p; + } else { + plim = hbp->hb_body + HBLKSIZE - sz; + } + /* Go through all words in block. */ + for (bit_no = 0; p <= plim; bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no)) { + ((struct enumerate_reachable_s *)ped)->proc(p, sz, + ((struct enumerate_reachable_s *)ped)->client_data); } + } } -GC_INNER void GC_mercury_enumerate_reachable_objects(void) +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( + GC_reachable_object_proc proc, + void *client_data) { - GC_ASSERT(GC_mercury_callback_reachable_object); - GC_apply_to_all_blocks(GC_mercury_do_enumerate_reachable_objects, (word)0); + struct enumerate_reachable_s ed; + + GC_ASSERT(I_HOLD_LOCK()); + ed.proc = proc; + ed.client_data = client_data; + GC_apply_to_all_blocks(GC_do_enumerate_reachable_objects, (word)&ed); } diff --git a/tests/test.c b/tests/test.c index 9607bddf..a8604b84 100644 --- a/tests/test.c +++ b/tests/test.c @@ -260,9 +260,10 @@ sexpr cons (sexpr x, sexpr y) } # endif +#include "gc_mark.h" + #ifdef GC_GCJ_SUPPORT -#include "gc_mark.h" #include "gc_gcj.h" /* The following struct emulates the vtable in gcj. */ @@ -1396,6 +1397,32 @@ void run_one_test(void) GC_log_printf("Finished %p\n", (void *)&start_time); } +void GC_CALLBACK reachable_objs_counter(void *obj, size_t size, + void *pcounter) +{ + if (0 == size) { + GC_printf("Reachable object has zero size\n"); + FAIL; + } + if (GC_base(obj) != obj) { + GC_printf("Invalid reachable object base passed by enumerator: %p\n", + obj); + FAIL; + } + if (GC_size(obj) != size) { + GC_printf("Invalid reachable object size passed by enumerator: %lu\n", + (unsigned long)size); + FAIL; + } + (*(unsigned *)pcounter)++; +} + +void * GC_CALLBACK reachable_objs_count_enumerator(void *pcounter) +{ + GC_enumerate_reachable_objects_inner(reachable_objs_counter, pcounter); + return NULL; +} + #define NUMBER_ROUND_UP(v, bound) ((((v) + (bound) - 1) / (bound)) * (bound)) void check_heap_stats(void) @@ -1411,6 +1438,7 @@ void check_heap_stats(void) int late_finalize_count = 0; # endif # endif + unsigned obj_count = 0; # ifdef VERY_SMALL_CONFIG /* The upper bounds are a guess, which has been empirically */ @@ -1468,6 +1496,8 @@ void check_heap_stats(void) FAIL; } } + (void)GC_call_with_alloc_lock(reachable_objs_count_enumerator, + &obj_count); GC_printf("Completed %u tests\n", n_tests); GC_printf("Allocated %d collectable objects\n", collectable_count); GC_printf("Allocated %d uncollectable objects\n", @@ -1540,6 +1570,7 @@ void check_heap_stats(void) (unsigned long)max_heap_sz); FAIL; } + GC_printf("Final number of reachable objects is %u\n", obj_count); # ifndef GC_GET_HEAP_USAGE_NOT_NEEDED /* Get global counters (just to check the functions work). */