Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 91 additions & 45 deletions src/gc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<finalizers.length;i++) {
var fin = finalizers[i].finalizer;
if(fin !== null && !IS_MARKED(fin)) h$follow(fin);
}
h$markRetained();
h$clearWeaks();
h$scannedWeaks = [];
h$finalizeWeaks(toFinalize);
h$weakPointerList = [];

h$finalizeCAFs(); // restore all unreachable CAFs to unevaluated state

Expand All @@ -256,33 +246,96 @@ function h$gc(t) {
}

function h$markRetained() {
var iter, marked, c, mark = h$gcMark;
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
marked (i.e. reachable), then mark all heap reachable from its value
or its finalizer, wand move the weak pointer object to a new list
*/
do {
TRACE_GC("mark retained iteration");
TRACE_GC("mark retained iteration 1/2");
marked = false;
// mark all finalizers of weak references where the key is reachable
iter = h$finalizers.iter();
while((c = iter.next()) !== null) {
if(!IS_MARKED(c.finalizer) && c.m.m === mark) {
TRACE_GC("recursively marking weak finalizer: " + h$collectProps(c.finalizer));
h$follow(c.finalizer);
marked = true;
}
}

// mark weak values for reachable keys
for(var i=h$scannedWeaks.length-1;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) {
// don't handle items deleted in earlier iteration
continue;
}
if (IS_MARKED(w.keym)) {
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);
// 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;
}
}

/*
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) {
// don't handle items deleted in step 2
continue;
}

TRACE_GC("mark retained iteration 2/2");

if (IS_MARKED(w)) {
if(w.val !== null) {
w.val = null;
}
if (w.finalizer !== null) {
if (!IS_MARKED(w.finalizer))
h$follow(w.finalizer);

toFinalize.push(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) {
Expand Down Expand Up @@ -375,15 +428,8 @@ function h$follow(obj, sp) {
for(var i=0;i<s.length;i++) ADDW(s[i]);
}
} else if(c instanceof h$Weak) {
TRACE_GC("marking weak reference");
if(c.keym.m === mark) {
if(c.val !== null && !IS_MARKED(c.val)) ADDW(c.val);
} else {
// fixme we should keep separate arrays for
// value mark pending / cleanup pending?
if(c.val !== null) h$scannedWeaks.push(c);
}
MARK_OBJ(c);
TRACE_GC("selecting weak reference");
h$weakPointerList.push(c)
} else if(c instanceof h$MVar) {
TRACE_GC("marking MVar");
MARK_OBJ(c);
Expand Down
21 changes: 4 additions & 17 deletions src/weak.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
// contains all pending finalizers
var h$finalizers = new h$Set();

// filled at scan time, weak refs with possible work to do
var h$scannedWeaks = [];
var h$weakPointerList = [];

#ifdef GHCJS_TRACE_WEAK
function h$traceWeak() { h$log.apply(h$log, arguments) }
Expand All @@ -15,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) {
Expand All @@ -46,8 +35,6 @@ function h$finalizeWeaks() {
}
h$wakeupThread(t);
}
return toFinalize;

}

// clear references for reachable weak refs with unreachable keys
Expand Down