You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

664 lines
20 KiB

12 years ago
/*
* Reference counting implementation.
*/
#include "duk_internal.h"
#ifdef DUK_USE_REFERENCE_COUNTING
#ifndef DUK_USE_DOUBLE_LINKED_HEAP
#error internal error, reference counting requires a double linked heap
#endif
/*
* Misc
*/
DUK_LOCAL void duk__queue_refzero(duk_heap *heap, duk_heaphdr *hdr) {
12 years ago
/* tail insert: don't disturb head in case refzero is running */
if (heap->refzero_list != NULL) {
duk_heaphdr *hdr_prev;
hdr_prev = heap->refzero_list_tail;
DUK_ASSERT(hdr_prev != NULL);
DUK_ASSERT(DUK_HEAPHDR_GET_NEXT(heap, hdr_prev) == NULL);
12 years ago
DUK_HEAPHDR_SET_NEXT(heap, hdr, NULL);
DUK_HEAPHDR_SET_PREV(heap, hdr, hdr_prev);
DUK_HEAPHDR_SET_NEXT(heap, hdr_prev, hdr);
DUK_ASSERT_HEAPHDR_LINKS(heap, hdr);
DUK_ASSERT_HEAPHDR_LINKS(heap, hdr_prev);
12 years ago
heap->refzero_list_tail = hdr;
} else {
DUK_ASSERT(heap->refzero_list_tail == NULL);
DUK_HEAPHDR_SET_NEXT(heap, hdr, NULL);
DUK_HEAPHDR_SET_PREV(heap, hdr, NULL);
DUK_ASSERT_HEAPHDR_LINKS(heap, hdr);
12 years ago
heap->refzero_list = hdr;
heap->refzero_list_tail = hdr;
}
}
/*
* Heap object refcount finalization.
*
* When an object is about to be freed, all other objects it refers to must
* be decref'd. Refcount finalization does NOT free the object or its inner
* allocations (mark-and-sweep shares these helpers), it just manipulates
* the refcounts.
*
* Note that any of the decref's may cause a refcount to drop to zero, BUT
* it will not be processed inline; instead, because refzero is already
* running, the objects will just be queued to refzero list and processed
* later. This eliminates C recursion.
*/
DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) {
11 years ago
duk_uint_fast32_t i;
12 years ago
DUK_ASSERT(h);
DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT);
/* XXX: better to get base and walk forwards? */
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it's not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for 'strs' built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -> DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer "usable_size" to "alloc_size" throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject 'p' to 'props', heap->st to heap->strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don't properly check for sizes) - Other minor cleanups
10 years ago
for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
duk_hstring *key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i);
12 years ago
if (!key) {
continue;
}
duk_heaphdr_decref(thr, (duk_heaphdr *) key);
if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i)) {
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, h, i));
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, h, i));
12 years ago
} else {
duk_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i));
12 years ago
}
}
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
duk_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i));
12 years ago
}
/* hash part is a 'weak reference' and does not contribute */
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h));
12 years ago
/* XXX: rearrange bits to allow a switch case to be used here? */
/* XXX: add a fast path for objects (and arrays)? */
/* DUK_HOBJECT_IS_ARRAY(h): needs no special handling now as there are
* no extra fields in need of decref.
*/
if (DUK_HOBJECT_IS_COMPFUNC(h)) {
duk_hcompfunc *f = (duk_hcompfunc *) h;
12 years ago
duk_tval *tv, *tv_end;
duk_hobject **funcs, **funcs_end;
DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL); /* compiled functions must be created 'atomically' */
12 years ago
tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f);
tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, f);
12 years ago
while (tv < tv_end) {
duk_tval_decref(thr, tv);
12 years ago
tv++;
}
funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f);
funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f);
12 years ago
while (funcs < funcs_end) {
duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs);
12 years ago
funcs++;
}
duk_heaphdr_decref(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_DATA(thr->heap, f));
} else if (DUK_HOBJECT_IS_NATFUNC(h)) {
duk_hnatfunc *f = (duk_hnatfunc *) h;
DUK_UNREF(f);
12 years ago
/* nothing to finalize */
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
} else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
duk_hbufobj *b = (duk_hbufobj *) h;
if (b->buf) {
duk_heaphdr_decref(thr, (duk_heaphdr *) b->buf);
}
#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
12 years ago
} else if (DUK_HOBJECT_IS_THREAD(h)) {
duk_hthread *t = (duk_hthread *) h;
duk_tval *tv;
tv = t->valstack;
while (tv < t->valstack_top) {
duk_tval_decref(thr, tv);
12 years ago
tv++;
}
for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
duk_activation *act = t->callstack + i;
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->var_env);
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->lex_env);
#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->prev_caller);
#endif
12 years ago
}
#if 0 /* nothing now */
for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) {
duk_catcher *cat = t->catchstack + i;
12 years ago
}
#endif
for (i = 0; i < DUK_NUM_BUILTINS; i++) {
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->builtins[i]);
12 years ago
}
duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->resumer);
12 years ago
}
}
DUK_INTERNAL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr) {
12 years ago
DUK_ASSERT(hdr);
switch (DUK_HEAPHDR_GET_TYPE(hdr)) {
12 years ago
case DUK_HTYPE_OBJECT:
duk__refcount_finalize_hobject(thr, (duk_hobject *) hdr);
12 years ago
break;
case DUK_HTYPE_BUFFER:
/* nothing to finalize */
break;
case DUK_HTYPE_STRING:
/* cannot happen: strings are not put into refzero list (they don't even have the next/prev pointers) */
default:
DUK_UNREACHABLE();
12 years ago
}
}
#if defined(DUK_USE_FINALIZER_SUPPORT)
#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 refcount 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_NATFUNC(obj)) {
if (((duk_hnatfunc *) obj)->func == duk__refcount_fake_finalizer) {
DUK_DD(DUK_DDPRINT("avoid fake torture finalizer for duk__refcount_fake_finalizer itself"));
return;
}
}
/* Avoid fake finalization when callstack limit has been reached.
* Otherwise a callstack limit error will be created, then refzero'ed,
* and we're in an infinite loop.
*/
if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit ||
thr->callstack_size + 2 * DUK_CALLSTACK_GROW_STEP >= thr->callstack_max /*approximate*/) {
DUK_D(DUK_DPRINT("call recursion depth reached, avoid fake torture finalizer"));
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 */
#endif /* DUK_USE_FINALIZER_SUPPORT */
12 years ago
/*
* Refcount memory freeing loop.
*
* Frees objects in the refzero_pending list until the list becomes
* empty. When an object is freed, its references get decref'd and
* may cause further objects to be queued for freeing.
*
* This could be expanded to allow incremental freeing: just bail out
* early and resume at a future alloc/decref/refzero.
*/
DUK_INTERNAL void duk_refzero_free_pending(duk_hthread *thr) {
12 years ago
duk_heaphdr *h1, *h2;
duk_heap *heap;
duk_int_t count = 0;
12 years ago
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
heap = thr->heap;
DUK_ASSERT(heap != NULL);
/*
* Detect recursive invocation
*/
if (DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap)) {
DUK_DDD(DUK_DDDPRINT("refzero free running, skip run"));
12 years ago
return;
}
/*
* Churn refzero_list until empty
*/
DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap);
while (heap->refzero_list) {
duk_hobject *obj;
#if defined(DUK_USE_FINALIZER_SUPPORT)
duk_bool_t rescued = 0;
#endif /* DUK_USE_FINALIZER_SUPPORT */
12 years ago
/*
* Pick an object from the head (don't remove yet).
*/
h1 = heap->refzero_list;
obj = (duk_hobject *) h1;
DUK_DD(DUK_DDPRINT("refzero processing %p: %!O", (void *) h1, (duk_heaphdr *) h1));
DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, h1) == NULL);
12 years ago
DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h1) == DUK_HTYPE_OBJECT); /* currently, always the case */
#if defined(DUK_USE_FINALIZER_SUPPORT)
#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 /* DUK_USE_REFZERO_FINALIZER_TORTURE */
#endif /* DUK_USE_FINALIZER_SUPPORT */
12 years ago
/*
* Finalizer check.
*
* Note: running a finalizer may have arbitrary side effects, e.g.
* queue more objects on refzero_list (tail), or even trigger a
* mark-and-sweep.
*
* Note: quick reject check should match vast majority of
* objects and must be safe (not throw any errors, ever).
*
* An object may have FINALIZED here if it was finalized by mark-and-sweep
* on a previous run and refcount then decreased to zero. We won't run the
* finalizer again here.
*
* A finalizer is looked up from the object and up its prototype chain
* (which allows inherited finalizers).
12 years ago
*/
#if defined(DUK_USE_FINALIZER_SUPPORT)
12 years ago
if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) {
DUK_DDD(DUK_DDDPRINT("object has a finalizer, run it"));
12 years ago
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h1) == 0);
DUK_HEAPHDR_PREINC_REFCOUNT(h1); /* bump refcount to prevent refzero during finalizer processing */
12 years ago
duk_hobject_run_finalizer(thr, obj); /* must never longjmp */
DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(h1)); /* duk_hobject_run_finalizer() sets */
12 years ago
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
DUK_HEAPHDR_PREDEC_REFCOUNT(h1); /* remove artificial bump */
11 years ago
DUK_ASSERT_DISABLE(h1->h_refcount >= 0); /* refcount is unsigned, so always true */
12 years ago
16-bit fields and heap pointer compression work Memory optimization work for very low memory devices (96 to 256kB system RAM). Overall changes are: - 16-bit fields for various internal structures to reduce their size - Heap pointer compression to reduce pointer size to 16 bits When DUK_OPT_LIGHTFUNC_BUILTINS and the new low memory options are enabled, Duktape initial heap memory usage is about 23kB (compared to baseline of about 45kB) on x86. Unless low memory feature options are enabled, there should be no visible changes to Duktape behavior. More detailed changes: - 16-bit changes for duk_heaphdr: pointer compression, refcount - 16-bit changes for duk_hstring: hash, blen, and clen can all be 16 bits, use 0xFFFF as string byte length limit (call sites ensure this limit is never exceeded) - 16-bit changes for duk_hbuffer, use 0xFFFF as buffer length limit - 16-bit fields for hobject size (entry part, array part), drop hash part since it&#39;s not usually needed for extremely low memory environments - 16-bit changes for duk_hcompiledfunction - Heap pointer packing for stringtable - Heap pointer packing for &#39;strs&#39; built-in strings list (saves around 600 to 700 bytes but may not be a good tradeoff because call site size will increase) Other changes: - Heaphdr NULL init fix. The original macros were broken: the double/single linked macro variants were the wrong way around. Now sets through macro to work properly with compressed pointers. - Rename duk_hbuffer CURR_DATA_PTR -&gt; DATA_PTR to reduce macro length (previous name was tediously long) - Rename buffer &#34;usable_size&#34; to &#34;alloc_size&#34; throughout as they have been the same for a while now (they used to differ when buffer had an extra NUL). - Add memory optimization markers to Duktape.env (pointer compression and individual 16-bit field options) - Rename a few internal fields for clarity: duk_hobject &#39;p&#39; to &#39;props&#39;, heap-&gt;st to heap-&gt;strtable - Add a safety check for buffer alloc size (should not be triggered but prevents wrapping if call sites don&#39;t properly check for sizes) - Other minor cleanups
10 years ago
if (DUK_HEAPHDR_GET_REFCOUNT(h1) != 0) {
DUK_DDD(DUK_DDDPRINT("-> object refcount after finalization non-zero, object will be rescued"));
12 years ago
rescued = 1;
} else {
DUK_DDD(DUK_DDDPRINT("-> object refcount still zero after finalization, object will be freed"));
12 years ago
}
}
#endif /* DUK_USE_FINALIZER_SUPPORT */
12 years ago
/* Refzero head is still the same. This is the case even if finalizer
12 years ago
* inserted more refzero objects; they are inserted to the tail.
*/
DUK_ASSERT(h1 == heap->refzero_list);
/*
* Remove the object from the refzero list. This cannot be done
* before a possible finalizer has been executed; the finalizer
* may trigger a mark-and-sweep, and mark-and-sweep must be able
* to traverse a complete refzero_list.
*/
h2 = DUK_HEAPHDR_GET_NEXT(heap, h1);
12 years ago
if (h2) {
DUK_HEAPHDR_SET_PREV(heap, h2, NULL); /* not strictly necessary */
12 years ago
heap->refzero_list = h2;
} else {
heap->refzero_list = NULL;
heap->refzero_list_tail = NULL;
}
/*
* Rescue or free.
*/
#if defined(DUK_USE_FINALIZER_SUPPORT)
12 years ago
if (rescued) {
/* yes -> move back to heap allocated */
DUK_DD(DUK_DDPRINT("object rescued during refcount finalization: %p", (void *) h1));
DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(h1));
DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(h1));
DUK_HEAPHDR_CLEAR_FINALIZED(h1);
h2 = heap->heap_allocated;
DUK_HEAPHDR_SET_PREV(heap, h1, NULL);
if (h2) {
DUK_HEAPHDR_SET_PREV(heap, h2, h1);
}
DUK_HEAPHDR_SET_NEXT(heap, h1, h2);
DUK_ASSERT_HEAPHDR_LINKS(heap, h1);
DUK_ASSERT_HEAPHDR_LINKS(heap, h2);
12 years ago
heap->heap_allocated = h1;
} else
#endif /* DUK_USE_FINALIZER_SUPPORT */
{
12 years ago
/* no -> decref members, then free */
duk__refcount_finalize_hobject(thr, obj);
12 years ago
duk_heap_free_heaphdr_raw(heap, h1);
}
count++;
}
DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap);
DUK_DDD(DUK_DDDPRINT("refzero processed %ld objects", (long) count));
12 years ago
/*
* Once the whole refzero cascade has been freed, check for
* a voluntary mark-and-sweep.
*/
#if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_VOLUNTARY_GC)
/* 'count' is more or less comparable to normal trigger counter update
* which happens in memory block (re)allocation.
*/
12 years ago
heap->mark_and_sweep_trigger_counter -= count;
if (heap->mark_and_sweep_trigger_counter <= 0) {
duk_bool_t rc;
duk_small_uint_t flags = 0; /* not emergency */
DUK_D(DUK_DPRINT("refcount triggering mark-and-sweep"));
rc = duk_heap_mark_and_sweep(heap, flags);
DUK_UNREF(rc);
DUK_D(DUK_DPRINT("refcount triggered mark-and-sweep => rc %ld", (long) rc));
12 years ago
}
#endif /* DUK_USE_MARK_AND_SWEEP && DUK_USE_VOLUNTARY_GC */
12 years ago
}
/*
* Incref and decref functions.
*
* Decref may trigger immediate refzero handling, which may free and finalize
* an arbitrary number of objects.
*/
DUK_LOCAL DUK_ALWAYS_INLINE void duk__heaphdr_refzero_helper(duk_hthread *thr, duk_heaphdr *h, duk_bool_t skip_free_pending) {
12 years ago
duk_heap *heap;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(h != NULL);
12 years ago
heap = thr->heap;
DUK_DDD(DUK_DDDPRINT("refzero %p: %!O", (void *) h, (duk_heaphdr *) h));
12 years ago
/*
* Refzero handling is skipped entirely if (1) mark-and-sweep is
* running or (2) execution is paused in the debugger. The objects
* are left in the heap, and will be freed by mark-and-sweep or
* eventual heap destruction.
*
* This is necessary during mark-and-sweep because refcounts are also
* updated during the sweep phase (otherwise objects referenced by a
* swept object would have incorrect refcounts) which then calls here.
* This could be avoided by using separate decref macros in
* mark-and-sweep; however, mark-and-sweep also calls finalizers which
* would use the ordinary decref macros anyway and still call this
* function.
12 years ago
*
* This check must be enabled also when mark-and-sweep support has been
* disabled: the flag is also used in heap destruction when running
* finalizers for remaining objects, and the flag prevents objects from
* being moved around in heap linked lists.
12 years ago
*/
/* XXX: ideally this would be just one flag (maybe a derived one) so
* that a single bit test is sufficient to check the condition.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
if (DUK_UNLIKELY(DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap) || DUK_HEAP_IS_PAUSED(heap))) {
#else
if (DUK_UNLIKELY(DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap))) {
#endif
DUK_DDD(DUK_DDDPRINT("refzero handling suppressed when mark-and-sweep running, object: %p", (void *) h));
12 years ago
return;
}
switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) {
12 years ago
case DUK_HTYPE_STRING:
/* Strings have no internal references but do have "weak"
* references in the string cache. Also note that strings
* are not on the heap_allocated list like other heap
* elements.
12 years ago
*/
duk_heap_strcache_string_remove(heap, (duk_hstring *) h);
duk_heap_string_remove(heap, (duk_hstring *) h);
#if 0
12 years ago
duk_heap_free_heaphdr_raw(heap, h);
#endif
duk_free_hstring_inner(heap, (duk_hstring *) h);
DUK_FREE(heap, h);
12 years ago
break;
case DUK_HTYPE_OBJECT:
/* Objects have internal references. Must finalize through
* the "refzero" work list.
12 years ago
*/
duk_heap_remove_any_from_heap_allocated(heap, h);
duk__queue_refzero(heap, h);
if (!skip_free_pending) {
duk_refzero_free_pending(thr);
}
12 years ago
break;
case DUK_HTYPE_BUFFER:
/* Buffers have no internal references. However, a dynamic
* buffer has a separate allocation for the buffer. This is
* freed by duk_heap_free_heaphdr_raw().
12 years ago
*/
duk_heap_remove_any_from_heap_allocated(heap, h);
#if 0
12 years ago
duk_heap_free_heaphdr_raw(heap, h);
#endif
duk_free_hbuffer_inner(heap, (duk_hbuffer *) h);
DUK_FREE(heap, h);
12 years ago
break;
default:
DUK_D(DUK_DPRINT("invalid heap type in decref: %ld", (long) DUK_HEAPHDR_GET_TYPE(h)));
DUK_UNREACHABLE();
12 years ago
}
}
DUK_INTERNAL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) {
duk__heaphdr_refzero_helper(thr, h, 0 /*skip_free_pending*/);
}
DUK_INTERNAL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h) {
duk__heaphdr_refzero_helper(thr, h, 1 /*skip_free_pending*/);
}
#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
DUK_INTERNAL void duk_tval_incref(duk_tval *tv) {
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT_DISABLE(h->h_refcount >= 0);
DUK_HEAPHDR_PREINC_REFCOUNT(h);
}
}
#endif
#if 0 /* unused */
DUK_INTERNAL void duk_tval_incref_allownull(duk_tval *tv) {
if (tv == NULL) {
return;
}
if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT_DISABLE(h->h_refcount >= 0);
DUK_HEAPHDR_PREINC_REFCOUNT(h);
}
}
#endif
DUK_INTERNAL void duk_tval_decref(duk_hthread *thr, duk_tval *tv) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
return;
}
duk_heaphdr_refzero(thr, h);
}
}
DUK_INTERNAL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
return;
}
duk_heaphdr_refzero_norz(thr, h);
}
}
#if 0 /* unused */
DUK_INTERNAL void duk_tval_decref_allownull(duk_hthread *thr, duk_tval *tv) {
DUK_ASSERT(thr != NULL);
if (tv == NULL) {
return;
}
if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv)) {
duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
duk_heaphdr_decref(thr, h);
}
}
#endif
#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
DUK_INTERNAL void duk_heaphdr_incref(duk_heaphdr *h) {
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0);
DUK_HEAPHDR_PREINC_REFCOUNT(h);
}
#endif
#if 0 /* unused */
DUK_INTERNAL void duk_heaphdr_incref_allownull(duk_heaphdr *h) {
if (h == NULL) {
return;
}
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0);
DUK_HEAPHDR_PREINC_REFCOUNT(h);
}
#endif
DUK_INTERNAL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
#if defined(DUK_USE_ROM_OBJECTS)
if (DUK_HEAPHDR_HAS_READONLY(h)) {
return;
}
#endif
if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
return;
}
duk_heaphdr_refzero(thr, h);
}
DUK_INTERNAL void duk_heaphdr_decref_allownull(duk_hthread *thr, duk_heaphdr *h) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
if (h == NULL) {
return;
}
DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
#if defined(DUK_USE_ROM_OBJECTS)
if (DUK_HEAPHDR_HAS_READONLY(h)) {
return;
}
#endif
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1);
if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) {
return;
}
duk_heaphdr_refzero(thr, h);
}
12 years ago
#else
/* no refcounting */
#endif /* DUK_USE_REFERENCE_COUNTING */