Compare commits

...

11 Commits

Author SHA1 Message Date
Don Isaac
27b8a3afd4 do not drain nextTick if there's a pending termination exception 2025-04-22 14:57:44 -07:00
Don Isaac
e6048407c7 weak ref version sanity check 2025-04-22 14:49:13 -07:00
Don Isaac
3d95c06160 Merge branch 'don/experiment/no-release-weakref-twice' of github.com:oven-sh/bun into don/experiment/no-release-weakref-twice 2025-04-22 14:30:27 -07:00
Don Isaac
3a7eda56ae wups 2025-04-22 14:30:22 -07:00
DonIsaac
45254bb487 bun run zig-format 2025-04-22 21:30:03 +00:00
Don Isaac
617d6033e8 fix: globalObject.reload() should drain nextTick queue 2025-04-22 14:28:31 -07:00
Don Isaac
6f77c49381 fix: also drain vm's microtask queue when nextTick queue is active 2025-04-22 10:08:35 -07:00
Don Isaac
cf08d63a69 Merge branch 'main' of github.com:oven-sh/bun into don/experiment/no-release-weakref-twice 2025-04-22 09:50:40 -07:00
Don Isaac
2f32465aea cleanup 2025-04-20 22:47:55 -04:00
Don Isaac
82f023ce7b finalize sync js execution when process.nextTick is used 2025-04-20 19:57:16 -04:00
Don Isaac
b0126a20d5 experiment: do not release weak refs twice 2025-04-20 19:51:48 -04:00
5 changed files with 61 additions and 5 deletions

View File

@@ -216,9 +216,12 @@ pub const JSGlobalObject = opaque {
extern fn JSC__JSGlobalObject__reload(JSC__JSGlobalObject__ptr: *JSGlobalObject) void;
pub fn reload(this: *JSC.JSGlobalObject) void {
this.vm().drainMicrotasks();
this.drainMicrotasks();
this.vm().collectAsync();
// NOTE(@DonIsaac): This triggers a synchronous GC every other reload.
// sync collections wait for pending async ones to complete. Maybe we
// should move the above collectAsync call into the reload function.
JSC__JSGlobalObject__reload(this);
}
@@ -384,6 +387,30 @@ pub const JSGlobalObject = opaque {
);
}
/// Drain the microtask queue and, if active, the nextTick queue.
///
/// Prefer `EventLoop.drainMicrotasks` over this function, since that also
/// drains deferred tasks.
///
/// The microtask queue will be drained for as long as there are microtasks
/// in it. This means that if a microtask queues more microtasks, those will
/// be drained as well.
///
/// The `nextTick` queue only gets activated when a user calls
/// `process.nextTick`. When active, `nextTick` tasks get drained _after_
/// microtasks.
///
/// Draining will stop early if a microtask or nextTick task throws a
/// termination exception (e.g. a worker calls `process.exit()` within an
/// event listener run on next tick).
///
/// ## Safety
/// - Caller thread must hold the JSC API lock
pub fn drainMicrotasks(this: *JSGlobalObject) void {
JSC__JSGlobalObject__drainMicrotasks(this);
}
extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void;
extern fn Bun__Process__emitWarning(globalObject: *JSGlobalObject, warning: JSValue, @"type": JSValue, code: JSValue, ctor: JSValue) void;
pub fn emitWarning(globalObject: *JSGlobalObject, warning: JSValue, @"type": JSValue, code: JSValue, ctor: JSValue) JSError!void {
Bun__Process__emitWarning(globalObject, warning, @"type", code, ctor);

View File

@@ -83,9 +83,14 @@ void JSNextTickQueue::drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
}
if (!isEmpty()) {
if (UNLIKELY(vm.hasPendingTerminationException())) {
return;
}
if (mustResetContext) {
globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined());
}
// note: drainFn will drain vm microtask queue
auto* drainFn = internalField(2).get().getObject();
auto throwScope = DECLARE_THROW_SCOPE(vm);
MarkedArgumentBuffer drainArgs;

View File

@@ -167,9 +167,14 @@ pub const VM = opaque {
}
extern fn JSC__VM__drainMicrotasks(vm: *VM) void;
/// Drain all microtasks from the microtask queue.
///
/// This does _not_ drain the nextTick queue. Use
/// `JSGlobalObject.drainMicrotasks` for that.
pub fn drainMicrotasks(
vm: *VM,
) void {
JSC.markMemberBinding("VM", @src());
return JSC__VM__drainMicrotasks(vm);
}

View File

@@ -3945,13 +3945,34 @@ extern "C" bool JSC__JSGlobalObject__startRemoteInspector(JSC__JSGlobalObject* g
void GlobalObject::drainMicrotasks()
{
auto& vm = this->vm();
#if BUN_DEBUG
uintptr_t currentGeneration = vm.currentWeakRefVersion();
#endif
if (auto nextTickQueue = this->m_nextTickQueue.get()) {
Bun::JSNextTickQueue* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueue);
queue->drain(vm, this);
#if BUN_DEBUG
// microtask queue draining may stop early when a
// termination exception is thrown within a microtask
if (!vm.hasPendingTerminationException()) {
vm.m_microtaskQueues.forEach([](JSC::MicrotaskQueue* queue) {
ASSERT(queue->isEmpty());
});
}
ASSERT(vm.currentWeakRefVersion() > currentGeneration);
#endif
return;
}
vm.drainMicrotasks();
#if BUN_DEBUG
ASSERT(vm.currentWeakRefVersion() > currentGeneration);
#endif
}
extern "C" void JSC__JSGlobalObject__drainMicrotasks(Zig::GlobalObject* globalObject)

View File

@@ -918,12 +918,10 @@ pub const EventLoop = struct {
}
}
extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void;
pub fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject, jsc_vm: *JSC.VM) void {
pub fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject, _: *JSC.VM) void {
JSC.markBinding(@src());
jsc_vm.releaseWeakRefs();
JSC__JSGlobalObject__drainMicrotasks(globalObject);
globalObject.drainMicrotasks();
this.virtual_machine.is_inside_deferred_task_queue = true;
this.deferred_tasks.run();