From 6fafd9a446ab0369884b370b785e52bee7ab7aee Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Fri, 28 Aug 2015 10:15:09 +0300 Subject: [PATCH] Implement refzero finalizer torture When refzero runs, call a fake finalizer for every object (other than the fake finalizer itself) so ensure call-related side effects occur for every refzero. --- src/duk_heap_refcount.c | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/duk_heap_refcount.c b/src/duk_heap_refcount.c index 859e9334..b512dc4b 100644 --- a/src/duk_heap_refcount.c +++ b/src/duk_heap_refcount.c @@ -163,6 +163,51 @@ DUK_INTERNAL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *h } } +#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE) +DUK_LOCAL duk_ret_t duk__refcount_fake_finalizer(duk_context *ctx) { + DUK_UNREF(ctx); + DUK_D(DUK_DPRINT("fake torture finalizer executed")); +#if 0 + DUK_DD(DUK_DDPRINT("fake torture finalizer for: %!T", duk_get_tval(ctx, 0))); +#endif + /* Require a lot of stack to force a value stack grow/shrink. */ + duk_require_stack(ctx, 100000); + + /* XXX: do something to force a callstack grow/shrink, perhaps + * just a manual forced resize? + */ + return 0; +} + +DUK_LOCAL void duk__refcount_run_torture_finalizer(duk_hthread *thr, duk_hobject *obj) { + duk_context *ctx; + duk_int_t rc; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + ctx = (duk_context *) thr; + + /* Avoid fake finalization for the duk__refcount_fake_finalizer function + * itself, otherwise we're in infinite recursion. + */ + if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { + if (((duk_hnativefunction *) obj)->func == duk__refcount_fake_finalizer) { + DUK_DD(DUK_DDPRINT("avoid fake torture finalizer for duk__refcount_fake_finalizer itself")); + return; + } + } + + /* Run fake finalizer. Avoid creating new refzero queue entries + * so that we are not forced into a forever loop. + */ + duk_push_c_function(ctx, duk__refcount_fake_finalizer, 1 /*nargs*/); + duk_push_hobject(ctx, obj); + rc = duk_pcall(ctx, 1); + DUK_UNREF(rc); /* ignored */ + duk_pop(ctx); +} +#endif /* DUK_USE_REFZERO_FINALIZER_TORTURE */ + /* * Refcount memory freeing loop. * @@ -212,6 +257,20 @@ DUK_LOCAL void duk__refzero_free_pending(duk_hthread *thr) { DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, h1) == NULL); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h1) == DUK_HTYPE_OBJECT); /* currently, always the case */ +#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE) + /* Torture option to shake out finalizer side effect issues: + * make a bogus function call for every finalizable object, + * essentially simulating the case where everything has a + * finalizer. + */ + DUK_DD(DUK_DDPRINT("refzero torture enabled, fake finalizer")); + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h1) == 0); + DUK_HEAPHDR_PREINC_REFCOUNT(h1); /* bump refcount to prevent refzero during finalizer processing */ + duk__refcount_run_torture_finalizer(thr, obj); /* must never longjmp */ + DUK_HEAPHDR_PREDEC_REFCOUNT(h1); /* remove artificial bump */ + DUK_ASSERT_DISABLE(h1->h_refcount >= 0); /* refcount is unsigned, so always true */ +#endif + /* * Finalizer check. *