From 0c5d79026b0ea7cd0f73eaf85bd4c120ee25e029 Mon Sep 17 00:00:00 2001 From: Markus Barenhoff Date: Thu, 8 Oct 2015 10:31:13 +0200 Subject: [PATCH 1/5] implement the gc semantics more like ghc. --- src/gc.js | 128 ++++++++++++++++++++++++++++++++++------------------ src/weak.js | 3 +- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/src/gc.js b/src/gc.js index f07bd1f..b8de32e 100644 --- a/src/gc.js +++ b/src/gc.js @@ -229,18 +229,8 @@ function h$gc(t) { // clean up threads waiting on unreachable synchronization primitives h$resolveDeadlocks(); - // now everything has been marked, bring out your dead references - - // run finalizers for all weak references with unreachable keys - var finalizers = h$finalizeWeaks(); - h$clearWeaks(); - for(i=0;i=0;i--) { - var w = h$scannedWeaks[i]; - if(w.keym.m === mark && w.val !== null && !IS_MARKED(w.val)) { - TRACE_GC("marking weak value"); - h$follow(w.val); - marked = true; - } - } - // continue for a next round if we have marked something more - // note: this will be slow for very deep chains of weak refs, - // change this if that becomes a problem. + for (var i = 0; i < h$weakPointerList.length; ++i) { + w = h$weakPointerList[i]; + if (w === null) { + continue; + } + if (w.keym.m === mark) { + TRACE_GC("recursively marking weak: " + h$collectProps(c.finalizer)); + + if (w.val !== null && !IS_MARKED(w.val)) { + h$follow(w.val); + } + + if (w.finalizer !== null && !IS_MARKED(w.finalizer)) { + h$follow(w.finalizer); + } + + newList.push(w); + h$weakPointerList[i] = null; + marked = true; + } + } + + /* + 3. Repeat from step (2), until a complete scan of Weak Pointer List finds + no weak pointer object with a marked keym. + */ } while(marked); + + + /* + 4. Scan the Weak Pointer List again. If the weak pointer object is reachable + then tombstone it. If the weak pointer object has a finalizer then move + it to the Finalization Pending List, and mark all the heap reachable + from the finalizer. If the finalizer referes to the key (and/or value), + this step will "resurrect" it. + */ + for (var i = 0; i < h$weakPointerList.length; ++i) { + w = h$weakPointerList[i]; + if (w === null) { + continue; + } + + TRACE_GC("mark retained iteration 2/2"); + + if (w.val !== null) { + // FIXME: we should set v.val to null ??? aka tombstone + } + + if (w.finalizer !== null) { + toFinalize.push(w.finalizer); + if (!IS_MARKED(w.finalizer)) { + h$follow(w.finalizer); + } + } + } + + /* + 5. The list accumulated in step (3) becomes the new Weak Pointer List. + Mark any unreachable weak pointer objects on this list as reachable. + */ + h$weakPointerList = newList; + newList = null; + + for (var i = 0; i < h$weakPointerList.length; ++i) { + w = h$weakPointerList[i]; + if (!IS_MARKED(w)) { + MARK_OBJ(w); + } + } + + return toFinalize; } function h$markThread(t) { @@ -375,15 +422,8 @@ function h$follow(obj, sp) { for(var i=0;i Date: Thu, 8 Oct 2015 10:44:57 +0200 Subject: [PATCH 2/5] don't need the list of finalizers. h$finalizeWeak should detect unmarked finalizers. --- src/gc.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gc.js b/src/gc.js index b8de32e..3e3a547 100644 --- a/src/gc.js +++ b/src/gc.js @@ -248,7 +248,6 @@ function h$gc(t) { function h$markRetained() { var iter, marked, w, mark = h$gcMark; var newList = []; - var toFinalize = []; /* 2. Scan the Weak Pointer List. If a weak pointer object has a key that is @@ -303,12 +302,12 @@ function h$markRetained() { TRACE_GC("mark retained iteration 2/2"); - if (w.val !== null) { - // FIXME: we should set v.val to null ??? aka tombstone - } + // FIXME: we should set v.val to null ??? aka tombstone + //if (w.val !== null && !IS_MARKED(w.val)) { + // w.val = null; + //} if (w.finalizer !== null) { - toFinalize.push(w.finalizer); if (!IS_MARKED(w.finalizer)) { h$follow(w.finalizer); } @@ -328,8 +327,6 @@ function h$markRetained() { MARK_OBJ(w); } } - - return toFinalize; } function h$markThread(t) { From 4a517b6617c6d5d4fcb4df8e8f849c5ae2533a2f Mon Sep 17 00:00:00 2001 From: Markus Barenhoff Date: Thu, 8 Oct 2015 16:07:11 +0200 Subject: [PATCH 3/5] run only those finalizers which were detected in h$markRetained() --- src/gc.js | 22 ++++++++++++---------- src/weak.js | 18 +++--------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/gc.js b/src/gc.js index 3e3a547..ceffc59 100644 --- a/src/gc.js +++ b/src/gc.js @@ -220,7 +220,7 @@ function h$gc(t) { while((nt = iter.next()) !== null) h$follow(nt.root); // now we've marked all the regular Haskell data, continue marking weak references - h$markRetained(); + var toFinalize = h$markRetained(); // now all running threads and threads blocked on something that's excpected // to make them runnable at some point have been marked, including other threads @@ -229,7 +229,7 @@ function h$gc(t) { // clean up threads waiting on unreachable synchronization primitives h$resolveDeadlocks(); - h$finalizeWeaks(); + h$finalizeWeaks(toFinalize); h$weakPointerList = []; h$finalizeCAFs(); // restore all unreachable CAFs to unevaluated state @@ -248,6 +248,7 @@ function h$gc(t) { function h$markRetained() { var iter, marked, w, mark = h$gcMark; var newList = []; + var toFinalize = []; /* 2. Scan the Weak Pointer List. If a weak pointer object has a key that is @@ -263,7 +264,7 @@ function h$markRetained() { if (w === null) { continue; } - if (w.keym.m === mark) { + if (IS_MARKED(w.keym)) { TRACE_GC("recursively marking weak: " + h$collectProps(c.finalizer)); if (w.val !== null && !IS_MARKED(w.val)) { @@ -302,14 +303,13 @@ function h$markRetained() { TRACE_GC("mark retained iteration 2/2"); - // FIXME: we should set v.val to null ??? aka tombstone - //if (w.val !== null && !IS_MARKED(w.val)) { - // w.val = null; - //} - - if (w.finalizer !== null) { - if (!IS_MARKED(w.finalizer)) { + if (IS_MARKED(w)) { + if(w.val !== null) { + w.val = null; + } + if (w.finalizer !== null && !IS_MARKED(w.finalizer)) { h$follow(w.finalizer); + toFinalize.push(w.finalizer); } } } @@ -327,6 +327,8 @@ function h$markRetained() { MARK_OBJ(w); } } + + return toFinalize; } function h$markThread(t) { diff --git a/src/weak.js b/src/weak.js index 500dde3..d1c3810 100644 --- a/src/weak.js +++ b/src/weak.js @@ -14,20 +14,10 @@ function h$traceWeak() { h$log.apply(h$log, arguments) } #endif // called by the GC after marking the heap -function h$finalizeWeaks() { +function h$finalizeWeaks(toFinalize) { var mark = h$gcMark; - TRACE_WEAK("finalizeWeaks: " + mark + " " + h$finalizers.size()); - var i, w, toFinalize = []; - var iter = h$finalizers.iter(); - while((w = iter.next()) !== null) { - TRACE_WEAK("checking weak with finalizer: " + h$stableNameInt(w.m)); - TRACE_WEAK("finalizer mark: " + w.m.m + " - " + mark); - if((w.m.m&3) !== mark) { - iter.remove(); - toFinalize.push(w); - TRACE_WEAK("finalizer scheduled"); - } - } + var i, w; + TRACE_WEAK("to finalize: " + toFinalize.length); // start a finalizer thread if any finalizers need to be run if(toFinalize.length > 0) { @@ -45,8 +35,6 @@ function h$finalizeWeaks() { } h$wakeupThread(t); } - return toFinalize; - } // clear references for reachable weak refs with unreachable keys From f4b35e8b8574024d0ed85d7cdb3c6a5abf83e9fa Mon Sep 17 00:00:00 2001 From: Markus Barenhoff Date: Thu, 8 Oct 2015 16:19:39 +0200 Subject: [PATCH 4/5] some more explaining comments --- src/gc.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gc.js b/src/gc.js index ceffc59..f1cde7b 100644 --- a/src/gc.js +++ b/src/gc.js @@ -262,6 +262,7 @@ function h$markRetained() { for (var i = 0; i < h$weakPointerList.length; ++i) { w = h$weakPointerList[i]; if (w === null) { + // don't handle items deleted in earlier iteration continue; } if (IS_MARKED(w.keym)) { @@ -276,7 +277,10 @@ function h$markRetained() { } newList.push(w); + // instead of removing the item from the h$weakpointerList + // we set it to null if we push it to newList. h$weakPointerList[i] = null; + marked = true; } } @@ -298,6 +302,7 @@ function h$markRetained() { for (var i = 0; i < h$weakPointerList.length; ++i) { w = h$weakPointerList[i]; if (w === null) { + // don't handle items deleted in step 2 continue; } From faa37cb537de3485261188152256359b5fc6052f Mon Sep 17 00:00:00 2001 From: Markus Barenhoff Date: Sun, 11 Oct 2015 15:23:54 +0200 Subject: [PATCH 5/5] fixed bug preventing finalizers from beeing run. --- src/gc.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gc.js b/src/gc.js index f1cde7b..1d7329c 100644 --- a/src/gc.js +++ b/src/gc.js @@ -312,8 +312,10 @@ function h$markRetained() { if(w.val !== null) { w.val = null; } - if (w.finalizer !== null && !IS_MARKED(w.finalizer)) { - h$follow(w.finalizer); + if (w.finalizer !== null) { + if (!IS_MARKED(w.finalizer)) + h$follow(w.finalizer); + toFinalize.push(w.finalizer); } }