Browse Source

Add mark-and-sweep refcount comparison asserts

* Add a h_assert_refcount field to duk_heaphdr when assertions are enabled.

* When doing mark-and-sweep, clear h_assert_refcount, perform mark-and-sweep
  processing normally, and assert for correct refcounts for objects that
  remain in heap_allocated after sweeping.  (Refcounts for objects prior to
  sweeping won't match those computed via reachability roots.)

* Improve FASTREFS asserts for refcounting and mark-and-sweep.
pull/1406/head
Sami Vaarala 8 years ago
parent
commit
1dca73ebd8
  1. 93
      src-input/duk_heap_markandsweep.c
  2. 2
      src-input/duk_heap_refcount.c
  3. 16
      src-input/duk_heaphdr.h
  4. 8
      src-input/duk_hobject.h

93
src-input/duk_heap_markandsweep.c

@ -71,8 +71,10 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) {
* subclass struct but nothing that needs marking in the subclass struct.
*/
if (DUK_HOBJECT_HAS_FASTREFS(h)) {
DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h));
return;
}
DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h));
if (DUK_HOBJECT_IS_COMPFUNC(h)) {
duk_hcompfunc *f = (duk_hcompfunc *) h;
@ -175,6 +177,9 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) {
DUK_DDD(DUK_DDDPRINT("readonly object %p, skip", (void *) h));
return;
}
#endif
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
h->h_assert_refcount++; /* Comparison refcount: bump even if already reachable. */
#endif
if (DUK_HEAPHDR_HAS_REACHABLE(h)) {
DUK_DDD(DUK_DDDPRINT("already marked reachable, skip"));
@ -405,6 +410,9 @@ DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) {
DUK_DDD(DUK_DDDPRINT("found a temp root: %p", (void *) hdr));
DUK_HEAPHDR_CLEAR_TEMPROOT(hdr);
DUK_HEAPHDR_CLEAR_REACHABLE(hdr); /* done so that duk__mark_heaphdr() works correctly */
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
hdr->h_assert_refcount--; /* Same node visited twice. */
#endif
duk__mark_heaphdr(heap, hdr);
#if defined(DUK_USE_DEBUG)
@ -1024,6 +1032,85 @@ DUK_LOCAL void duk__assert_valid_refcounts(duk_heap *heap) {
hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
}
}
DUK_LOCAL void duk__clear_assert_refcounts(duk_heap *heap) {
duk_heaphdr *curr;
duk_uint32_t i;
for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
curr->h_assert_refcount = 0;
}
for (curr = heap->finalize_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
curr->h_assert_refcount = 0;
}
for (curr = heap->refzero_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
curr->h_assert_refcount = 0;
}
for (i = 0; i < heap->st_size; i++) {
duk_hstring *h;
#if defined(DUK_USE_STRTAB_PTRCOMP)
h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]);
#else
h = heap->strtable[i];
#endif
while (h != NULL) {
((duk_heaphdr *) h)->h_assert_refcount = 0;
h = h->hdr.h_next;
}
}
}
DUK_LOCAL void duk__check_refcount_heaphdr(duk_heaphdr *hdr) {
duk_bool_t count_ok;
/* The refcount check only makes sense for reachable objects on
* heap_allocated or string table, after the sweep phase. Prior to
* sweep phase refcounts will include references that are not visible
* via reachability roots.
*
* Because we're called after the sweep phase, all heap objects on
* heap_allocated are reachable. REACHABLE flags have already been
* cleared so we can't check them.
*/
/* ROM objects have intentionally incorrect refcount (1), but we won't
* check them.
*/
DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr));
count_ok = ((duk_size_t) DUK_HEAPHDR_GET_REFCOUNT(hdr) == hdr->h_assert_refcount);
if (!count_ok) {
DUK_D(DUK_DPRINT("refcount mismatch for: %p: header=%ld counted=%ld --> %!iO",
(void *) hdr, (long) DUK_HEAPHDR_GET_REFCOUNT(hdr),
(long) hdr->h_assert_refcount, hdr));
DUK_ASSERT(0);
}
}
DUK_LOCAL void duk__check_assert_refcounts(duk_heap *heap) {
duk_heaphdr *curr;
duk_uint32_t i;
for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) {
duk__check_refcount_heaphdr(curr);
}
for (i = 0; i < heap->st_size; i++) {
duk_hstring *h;
#if defined(DUK_USE_STRTAB_PTRCOMP)
h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]);
#else
h = heap->strtable[i];
#endif
while (h != NULL) {
duk__check_refcount_heaphdr((duk_heaphdr *) h);
h = h->hdr.h_next;
}
}
}
#endif /* DUK_USE_REFERENCE_COUNTING */
#endif /* DUK_USE_ASSERTIONS */
@ -1177,6 +1264,9 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t
* previous run had finalizer skip flag.
*/
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
duk__clear_assert_refcounts(heap);
#endif
duk__mark_roots_heap(heap); /* main reachability roots */
#if defined(DUK_USE_REFERENCE_COUNTING)
duk__mark_refzero_list(heap); /* refzero_list treated as reachability roots */
@ -1210,6 +1300,9 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t
#endif
duk__sweep_heap(heap, flags, &count_keep_obj);
duk__sweep_stringtable(heap, &count_keep_str);
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
duk__check_assert_refcounts(heap);
#endif
#if defined(DUK_USE_REFERENCE_COUNTING)
duk__clear_refzero_list_flags(heap);
#endif

2
src-input/duk_heap_refcount.c

@ -121,8 +121,10 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h)
* duk_harray has additional fields, none of them need
* DECREF updates.
*/
DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h));
return;
}
DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h));
/* Slow path: special object, start bit checks from most likely. */

16
src-input/duk_heaphdr.h

@ -32,6 +32,12 @@ struct duk_heaphdr {
duk_uint32_t h_flags;
#if defined(DUK_USE_REFERENCE_COUNTING)
#if defined(DUK_USE_ASSERTIONS)
/* When assertions enabled, used by mark-and-sweep for refcount
* validation. Largest reasonable type; also detects overflows.
*/
duk_size_t h_assert_refcount;
#endif
#if defined(DUK_USE_REFCOUNT16)
duk_uint16_t h_refcount;
#elif defined(DUK_USE_REFCOUNT32)
@ -39,7 +45,7 @@ struct duk_heaphdr {
#else
duk_size_t h_refcount;
#endif
#endif
#endif /* DUK_USE_REFERENCE_COUNTING */
#if defined(DUK_USE_HEAPPTR16)
duk_uint16_t h_next16;
@ -79,6 +85,12 @@ struct duk_heaphdr_string {
duk_uint32_t h_flags;
#if defined(DUK_USE_REFERENCE_COUNTING)
#if defined(DUK_USE_ASSERTIONS)
/* When assertions enabled, used by mark-and-sweep for refcount
* validation. Largest reasonable type; also detects overflows.
*/
duk_size_t h_assert_refcount;
#endif
#if defined(DUK_USE_REFCOUNT16)
duk_uint16_t h_refcount;
duk_uint16_t h_strextra16; /* round out to 8 bytes */
@ -89,7 +101,7 @@ struct duk_heaphdr_string {
#endif
#else
duk_uint16_t h_strextra16;
#endif
#endif /* DUK_USE_REFERENCE_COUNTING */
duk_hstring *h_next;
/* No 'h_prev' pointer for strings. */

8
src-input/duk_hobject.h

@ -257,6 +257,14 @@
#define DUK_HOBJECT_CLEAR_EXOTIC_DUKFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC)
#define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
/* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond
* duk_hobject base header.
*/
#define DUK_HOBJECT_PROHIBITS_FASTREFS(h) \
(DUK_HOBJECT_IS_COMPFUNC((h)) || DUK_HOBJECT_IS_DECENV((h)) || DUK_HOBJECT_IS_OBJENV((h)) || \
DUK_HOBJECT_IS_BUFOBJ((h)) || DUK_HOBJECT_IS_THREAD((h)))
#define DUK_HOBJECT_ALLOWS_FASTREFS(h) (!DUK_HOBJECT_PROHIBITS_FASTREFS((h)))
/* Flags used for property attributes in duk_propdesc and packed flags.
* Must fit into 8 bits.
*/

Loading…
Cancel
Save