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
17 changes: 16 additions & 1 deletion src/fibers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ class Fiber {
* i.e. After fiber completes, while yielded, or before started
*/
void MakeWeak() {
if (handle.IsEmpty()) return; // garbage-collected fiber being unwound as a zombie
uni::MakeWeak<WeakCallback>(isolate, handle, (void*)this);
}

Expand All @@ -488,6 +489,7 @@ class Fiber {
* i.e. While running.
*/
void ClearWeak() {
if (handle.IsEmpty()) return; // see MakeWeak()
handle.ClearWeak();
}

Expand All @@ -508,7 +510,14 @@ class Fiber {
if (that.started) {
assert(that.yielding);
orphaned_fibers.push_back(&that);
#if V8_AT_LEAST(10, 4)
// kParameter is a phantom callback: V8 has already reclaimed the JS object and CHECKs that
// the handle was reset before this callback returns ("Handle not reset in first callback").
// The fiber is unwound and deleted by DestroyOrphans; there is no JS object to hand back.
uni::Dispose(that.isolate, that.handle);
#else
that.ClearWeak();
#endif
return;
}

Expand Down Expand Up @@ -550,7 +559,12 @@ class Fiber {
}

uni::Dispose(that.isolate, that.yielded);
#if V8_AT_LEAST(10, 4)
// The JS object is gone (see WeakCallback); nothing can reference this fiber again.
delete &that;
#else
that.MakeWeak();
#endif
}
}

Expand Down Expand Up @@ -851,7 +865,8 @@ class Fiber {
}

static uni::FunctionType GetCurrent(Local<String> property, const uni::GetterCallbackInfo& info) {
if (current) {
if (current && !current->handle.IsEmpty()) {
// The handle is empty while a garbage-collected fiber is being unwound as a zombie.
return uni::Return(current->handle, info);
} else {
return uni::Return(uni::Undefined(Isolate::GetCurrent()), info);
Expand Down
32 changes: 32 additions & 0 deletions test/orphan-gc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use strict";
// A yielded fiber whose JS object is garbage-collected must be unwound as a zombie, not crash the
// process. On V8 >= 10.4 the weak callback is a phantom (kParameter) callback and has to reset its
// handle; the old kFinalizer code path resurrected the object instead.
var Fiber = require('fibers');
var v8 = require('v8');
var vm = require('vm');
v8.setFlagsFromString('--expose_gc');
var gc = vm.runInNewContext('gc');

var N = 200, unwound = 0;
for (var ii = 0; ii < N; ++ii) {
var fiber = Fiber(function() {
try {
Fiber.yield();
} catch (err) {
// zombie exception. Touch Fiber.current: on V8 >= 10.4 the JS object is already gone and
// this must return undefined instead of dereferencing an empty handle.
Fiber.current;
++unwound;
throw err;
}
});
fiber.run();
fiber = null;
}
gc(); gc();
Fiber(function() {}).run(); // Fiber::Run() calls DestroyOrphans()
gc(); gc();
Fiber(function() {}).run();

console.log(unwound === N ? 'pass' : 'fail: unwound ' + unwound + '/' + N);