From 565e51091b7020c91e8c268ccfefbcbd6356f313 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Tue, 24 Mar 2020 06:49:25 +0000 Subject: [PATCH] fixed outstanding bugs in gc implementation. pending more improvements --- hawk/lib/hawk-prv.h | 2 +- hawk/lib/hawk.h | 5 ++ hawk/lib/mod-sys.c | 4 +- hawk/lib/rbt.c | 12 ++--- hawk/lib/run.c | 53 ++++++++++++++----- hawk/lib/val.c | 126 ++++++++++++++++++++++++++------------------ 6 files changed, 127 insertions(+), 75 deletions(-) diff --git a/hawk/lib/hawk-prv.h b/hawk/lib/hawk-prv.h index db44d68c..03892c37 100644 --- a/hawk/lib/hawk-prv.h +++ b/hawk/lib/hawk-prv.h @@ -48,7 +48,7 @@ typedef struct hawk_tree_t hawk_tree_t; #include "err-prv.h" #include "misc-prv.h" -/*#define HAWK_ENABLE_GC*/ +#define HAWK_ENABLE_GC #define HAWK_ENABLE_STR_CACHE #define HAWK_ENABLE_MBS_CACHE diff --git a/hawk/lib/hawk.h b/hawk/lib/hawk.h index f26016cb..489d0f16 100644 --- a/hawk/lib/hawk.h +++ b/hawk/lib/hawk.h @@ -3004,6 +3004,11 @@ HAWK_EXPORT void hawk_rtx_refdownval_nofree ( hawk_val_t* val /**< value pointer */ ); + +HAWK_EXPORT void hawk_rtx_gc ( + hawk_rtx_t* rtx +); + /** * The hawk_rtx_valtobool() function converts a value \a val to a boolean * value. diff --git a/hawk/lib/mod-sys.c b/hawk/lib/mod-sys.c index 73d08dba..7e64c38f 100644 --- a/hawk/lib/mod-sys.c +++ b/hawk/lib/mod-sys.c @@ -75,8 +75,8 @@ # define X_SOCK_CLOEXEC 0 /* 0 is effectless for a bit flag */ #endif -#if defined(SOCK_CLOEXEC) -# define X_SOCK_CLOEXEC SOCK_NONBLOCK +#if defined(SOCK_NONBLOCK) +# define X_SOCK_NONBLOCK SOCK_NONBLOCK #else # define X_SOCK_NONBLOCK 0 /* 0 is effectless for a bit flag */ #endif diff --git a/hawk/lib/rbt.c b/hawk/lib/rbt.c index 9eacbc0e..d5e10f83 100644 --- a/hawk/lib/rbt.c +++ b/hawk/lib/rbt.c @@ -109,10 +109,10 @@ HAWK_INLINE hawk_rbt_pair_t* hawk_rbt_allocpair ( } else { - VPTR(pair) = vcop (rbt, vptr, vlen); + VPTR(pair) = vcop(rbt, vptr, vlen); if (VPTR(pair) != HAWK_NULL) { - if (rbt->style->freeer[HAWK_RBT_KEY] != HAWK_NULL) + if (rbt->style->freeer[HAWK_RBT_KEY]) rbt->style->freeer[HAWK_RBT_KEY] (rbt, KPTR(pair), KLEN(pair)); hawk_gem_freemem (rbt->gem, pair); return HAWK_NULL; @@ -124,9 +124,9 @@ HAWK_INLINE hawk_rbt_pair_t* hawk_rbt_allocpair ( HAWK_INLINE void hawk_rbt_freepair (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair) { - if (rbt->style->freeer[HAWK_RBT_KEY] != HAWK_NULL) + if (rbt->style->freeer[HAWK_RBT_KEY]) rbt->style->freeer[HAWK_RBT_KEY] (rbt, KPTR(pair), KLEN(pair)); - if (rbt->style->freeer[HAWK_RBT_VAL] != HAWK_NULL) + if (rbt->style->freeer[HAWK_RBT_VAL]) rbt->style->freeer[HAWK_RBT_VAL] (rbt, VPTR(pair), VLEN(pair)); hawk_gem_freemem (rbt->gem, pair); } @@ -478,13 +478,13 @@ static hawk_rbt_pair_t* change_pair_val (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair, else { void* nvptr = vcop(rbt, vptr, vlen); - if (nvptr == HAWK_NULL) return HAWK_NULL; + if (HAWK_UNLIKELY(!nvptr)) return HAWK_NULL; VPTR(pair) = nvptr; VLEN(pair) = vlen; } /* free up the old value */ - if (rbt->style->freeer[HAWK_RBT_VAL] != HAWK_NULL) + if (rbt->style->freeer[HAWK_RBT_VAL]) { rbt->style->freeer[HAWK_RBT_VAL] (rbt, ovptr, ovlen); } diff --git a/hawk/lib/run.c b/hawk/lib/run.c index cffde516..41e66d36 100644 --- a/hawk/lib/run.c +++ b/hawk/lib/run.c @@ -1239,7 +1239,13 @@ static void fini_rtx (hawk_rtx_t* rtx, int fini_globals) hawk_ooecs_fini (&rtx->inrec.linew); hawk_ooecs_fini (&rtx->inrec.line); - if (fini_globals) refdown_globals (rtx, 1); + if (fini_globals) + { + refdown_globals (rtx, 1); + #if defined(HAWK_ENABLE_GC) + hawk_rtx_gc (rtx); + #endif + } /* destroy the stack if necessary */ if (rtx->stack) @@ -1605,7 +1611,7 @@ static hawk_val_t* run_bpae_loop (hawk_rtx_t* rtx) rtx->active_block = blk; rtx->exit_level = EXIT_NONE; - if (run_block (rtx, blk) <= -1) ret = -1; + if (run_block(rtx, blk) <= -1) ret = -1; else if (rtx->exit_level >= EXIT_GLOBAL) { /* once exit is called inside one of END blocks, @@ -1665,18 +1671,37 @@ hawk_val_t* hawk_rtx_loop (hawk_rtx_t* rtx) hawk_val_t* hawk_rtx_execwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* args[], hawk_oow_t nargs) { - return (rtx->hawk->parse.pragma.entry[0] != '\0')? + hawk_val_t* v; + + v = (rtx->hawk->parse.pragma.entry[0] != '\0')? hawk_rtx_callwithooucstrarr(rtx, rtx->hawk->parse.pragma.entry, args, nargs): hawk_rtx_loop(rtx); + +#if defined(HAWK_ENABLE_GC) + /* i assume this function is a usual hawk program starter. + * call garbage collection after a whole program finishes */ + //hawk_rtx_gc (rtx); +#endif + + return v; } hawk_val_t* hawk_rtx_execwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* args[], hawk_oow_t nargs) { - return (rtx->hawk->parse.pragma.entry[0] != '\0')? + hawk_val_t* v; + + v = (rtx->hawk->parse.pragma.entry[0] != '\0')? hawk_rtx_callwithoobcstrarr(rtx, rtx->hawk->parse.pragma.entry, args, nargs): hawk_rtx_loop(rtx); -} +#if defined(HAWK_ENABLE_GC) + /* i assume this function is a usual hawk program starter. + * call garbage collection after a whole program finishes */ + //hawk_rtx_gc (rtx); +#endif + + return v; +} /* find an AWK function by name */ static hawk_fun_t* find_fun (hawk_rtx_t* rtx, const hawk_ooch_t* name) @@ -1806,7 +1831,7 @@ hawk_val_t* hawk_rtx_callwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* name, haw hawk_fun_t* fun; fun = hawk_rtx_findfunwithucstr(rtx, name); - if (!fun) return HAWK_NULL; + if (HAWK_UNLIKELY(!fun)) return HAWK_NULL; return hawk_rtx_callfun(rtx, fun, args, nargs); } @@ -1827,12 +1852,12 @@ hawk_val_t* hawk_rtx_callwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_val_t** v, * ret; v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs); - if (!v) return HAWK_NULL; + if (HAWK_UNLIKELY(!v)) return HAWK_NULL; for (i = 0; i < nargs; i++) { v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]); - if (!v[i]) + if (HAWK_UNLIKELY(!v[i])) { ret = HAWK_NULL; goto oops; @@ -1858,12 +1883,12 @@ hawk_val_t* hawk_rtx_callwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_val_t** v, * ret; v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs); - if (!v) return HAWK_NULL; + if (HAWK_UNLIKELY(!v)) return HAWK_NULL; for (i = 0; i < nargs; i++) { v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]); - if (!v[i]) + if (HAWK_UNLIKELY(!v[i])) { ret = HAWK_NULL; goto oops; @@ -1889,12 +1914,12 @@ hawk_val_t* hawk_rtx_callwithooucstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* nam hawk_val_t** v, * ret; v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs); - if (!v) return HAWK_NULL; + if (HAWK_UNLIKELY(!v)) return HAWK_NULL; for (i = 0; i < nargs; i++) { v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]); - if (!v[i]) + if (HAWK_UNLIKELY(!v[i])) { ret = HAWK_NULL; goto oops; @@ -1920,12 +1945,12 @@ hawk_val_t* hawk_rtx_callwithoobcstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* nam hawk_val_t** v, * ret; v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs); - if (!v) return HAWK_NULL; + if (HAWK_UNLIKELY(!v)) return HAWK_NULL; for (i = 0; i < nargs; i++) { v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]); - if (!v[i]) + if (HAWK_UNLIKELY(!v[i])) { ret = HAWK_NULL; goto oops; diff --git a/hawk/lib/val.c b/hawk/lib/val.c index da34b7cc..57091716 100644 --- a/hawk/lib/val.c +++ b/hawk/lib/val.c @@ -42,7 +42,7 @@ hawk_val_t* hawk_val_zlm = (hawk_val_t*)&awk_zlm; #if defined(HAWK_ENABLE_GC) /* - BEGIN { +BEGIN { @local a, b, c, nil; for (i = 1; i < 10; i++) a[i] = i; a[11] = a; @@ -68,8 +68,25 @@ BEGIN { b[1] = a; c[1] = 0; } -* -* BEGIN { + +BEGIN { + @local a, b, c, j, nil; + j[1] = 20; + j[2] = 20; + for (i = 1; i < 10; i++) a[i] = i; + a[9] = j; + a[10] = "world"; + a[11] = a; + a[12] = a; + a[13] = "hello"; + j[3] = a; + a[14] = j; + a = nil; + b[1] = a; + c[1] = 0; +} + +BEGIN { @local a, b, c, nil; j[1] = 20; j[2] = 20; @@ -90,6 +107,7 @@ BEGIN { */ #define GCH_MOVED HAWK_TYPE_MAX(hawk_uintptr_t) +#define GCH_UNREACHABLE (GCH_MOVED - 1) static hawk_val_t* gc_calloc (hawk_rtx_t* rtx, hawk_oow_t size) { @@ -250,52 +268,45 @@ static void gc_free_unreachables (hawk_rtx_t* rtx, hawk_gch_t* list) { hawk_gch_t* gch; + /* there might be recursive cross references among unreachable values. + * simple traversal and sequential disposal causes various issues */ + + /* mark all unreabale values */ + gch = list->gc_next; + while (gch != list) + { + gch->gc_refs = GCH_UNREACHABLE; +//printf (">>>MARKED UNREACHABLE %p\n", gch); + gch = gch->gc_next; + } + + /* free the values without actually freeing the outer shell */ + gch = list->gc_next; + while (gch != list) + { + /* -9999 preserves the outer shell */ + hawk_rtx_freeval (rtx, hawk_gch_to_val(gch), -9999); + gch = gch->gc_next; + } + + /* free the outer shell forcibly*/ while (list->gc_next != list) { gch = list->gc_next; /* the first entry in the list */ -printf ("^^^^^^^^^^^ freeing %p gc_refs %d v_refs %d\n", gch, (int)gch->gc_refs, (int)hawk_gch_to_val(gch)->v_refs); -#if 0 - hawk_rtx_freeval (rtx, hawk_gch_to_val(gch), 0); -#else - { - // TODO: revise this. MAP ONLY as of now. - hawk_val_map_t* v = (hawk_val_map_t*)hawk_gch_to_val(gch); - hawk_map_pair_t* pair; - hawk_map_itr_t itr; - hawk_oow_t refs = 0; - - hawk_init_map_itr (&itr, 0); - pair = hawk_map_getfirstpair(v->map, &itr); - while (pair) - { - if (HAWK_MAP_VPTR(pair) == v) - { - refs++; - HAWK_MAP_VPTR(pair) = hawk_rtx_makenilval(rtx); - } - else - { - hawk_rtx_refdownval (rtx, HAWK_MAP_VPTR(pair)); - HAWK_MAP_VPTR(pair) = hawk_rtx_makenilval(rtx); - } - pair = hawk_map_getnextpair(v->map, &itr); - } - -//printf (" >>>>>>>> freeing %p val %p gc_refs %d v_refs %d refs %d\n", gch, v, (int)gch->gc_refs, (int)hawk_gch_to_val(gch)->v_refs, (int)refs); - while (refs-- > 0) hawk_rtx_refdownval (rtx, v); - -//printf (" >>>>>>>> freed %p\n", gch); - } -#endif + /* do what hawk_rtx_freeval() would do without -9999 */ + rtx->gc.all_count--; + gc_unchain_gch (gch); +//printf ("^^^^^^^^^^^ freeing %p gc_refs %d v_refs %d\n", gch, (int)gch->gc_refs, (int)hawk_gch_to_val(gch)->v_refs); + hawk_rtx_freemem (rtx, gch); } } -void gc_collect_garbage (hawk_rtx_t* rtx) +void hawk_rtx_gc (hawk_rtx_t* rtx) { hawk_gch_t reachable; -printf ("collecting garbage...\n"); +//printf ("collecting garbage...\n"); gc_trace_refs (&rtx->gc.all); reachable.gc_prev = &reachable; @@ -303,15 +314,15 @@ printf ("collecting garbage...\n"); gc_move_reachables (&rtx->gc.all, &reachable); /* only unreachables are left in rtx->gc.all */ -gc_dump_refs (rtx, &rtx->gc.all); +//gc_dump_refs (rtx, &rtx->gc.all); gc_free_unreachables (rtx, &rtx->gc.all); -gc_dump_refs (rtx, &rtx->gc.all); +//gc_dump_refs (rtx, &rtx->gc.all); HAWK_ASSERT (rtx->gc.all.gc_next == &rtx->gc.all); /* move all reachables back to the main list */ gc_move_all_gchs (&reachable, &rtx->gc.all); -printf ("collecting garbage.done ..\n"); +//printf ("collecting garbage.done ..\n"); } #endif @@ -799,17 +810,23 @@ hawk_val_t* hawk_rtx_makerexval (hawk_rtx_t* rtx, const hawk_oocs_t* str, hawk_t static void free_mapval (hawk_map_t* map, void* dptr, hawk_oow_t dlen) { hawk_rtx_t* rtx = *(hawk_rtx_t**)hawk_map_getxtn(map); + hawk_val_t* v = (hawk_val_t*)dptr; #if defined(DEBUG_VAL) - hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("refdown in map free - [%O]\n"), dptr); + hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_T("refdown in map free - [%O]\n"), v); #endif #if defined(HAWK_ENABLE_GC) - /* this part is not right i think.... revisit this ... */ - if (HAWK_RTX_GETVALTYPE(rtx, (hawk_val_t*)dptr) == HAWK_VAL_MAP && - ((hawk_val_map_t*)dptr)->map == map) return; + if (HAWK_VTR_IS_POINTER(v) && v->v_gc && hawk_val_to_gch(v)->gc_refs == GCH_UNREACHABLE) + { + /* do nothing if the element is unreachable. + * this behavior pairs up with gc_free_unreachables() to + * achieve safe disposal of a value */ + return; + } #endif - hawk_rtx_refdownval (rtx, dptr); + + hawk_rtx_refdownval (rtx, v); } static void same_mapval (hawk_map_t* map, void* dptr, hawk_oow_t dlen) @@ -847,7 +864,7 @@ hawk_val_t* hawk_rtx_makemapval (hawk_rtx_t* rtx) hawk_val_map_t* val; #if defined(HAWK_ENABLE_GC) -gc_collect_garbage(rtx); +hawk_rtx_gc(rtx); val = (hawk_val_map_t*)gc_calloc(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx)); #else val = (hawk_val_map_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx)); @@ -873,6 +890,7 @@ gc_collect_garbage(rtx); gc_chain_val (&rtx->gc.all, (hawk_val_t*)val); rtx->gc.all_count++; val->v_gc = 1; +//printf ("MADE GCH %p VAL %p\n", hawk_val_to_gch(val), val); #endif return (hawk_val_t*)val; @@ -1184,11 +1202,15 @@ void hawk_rtx_freeval (hawk_rtx_t* rtx, hawk_val_t* val, int cache) case HAWK_VAL_MAP: #if defined(HAWK_ENABLE_GC) -printf ("FREEING GCH %p VAL %p\n", hawk_val_to_gch(val), val); - rtx->gc.all_count--; - gc_unchain_val (val); +//printf ("FREEING GCH %p VAL %p\n", hawk_val_to_gch(val), val); + hawk_map_fini (((hawk_val_map_t*)val)->map); - hawk_rtx_freemem (rtx, hawk_val_to_gch(val)); + if (cache != -9999) + { + rtx->gc.all_count--; + gc_unchain_val (val); + hawk_rtx_freemem (rtx, hawk_val_to_gch(val)); + } #else hawk_map_fini (((hawk_val_map_t*)val)->map); hawk_rtx_freemem (rtx, val);