Skip to content

Commit

Permalink
Add AbortSignal.timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred-Sumner committed Dec 3, 2022
1 parent 9cc03cd commit fe4f39f
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 13 deletions.
9 changes: 9 additions & 0 deletions src/bun.js/bindings/ScriptExecutionContext.h
Expand Up @@ -132,6 +132,15 @@ class ScriptExecutionContext : public CanMakeWeakPtr<ScriptExecutionContext> {
reinterpret_cast<Zig::GlobalObject*>(m_globalObject)->queueTask(task);
} // Executes the task on context's thread asynchronously.

void postTaskOnTimeout(EventLoopTask* task, Seconds timeout)
{
reinterpret_cast<Zig::GlobalObject*>(m_globalObject)->queueTaskOnTimeout(task, static_cast<int>(timeout.milliseconds()));
} // Executes the task on context's thread asynchronously.
void postTaskOnTimeout(Function<void(ScriptExecutionContext&)>&& lambda, Seconds timeout)
{
auto* task = new EventLoopTask(WTFMove(lambda));
postTaskOnTimeout(task, timeout);
}
template<typename... Arguments>
void postCrossThreadTask(Arguments&&... arguments)
{
Expand Down
6 changes: 6 additions & 0 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Expand Up @@ -3368,6 +3368,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
}

extern "C" void Bun__queueTask(JSC__JSGlobalObject*, WebCore::EventLoopTask* task);
extern "C" void Bun__queueTaskWithTimeout(JSC__JSGlobalObject*, WebCore::EventLoopTask* task, int timeout);
extern "C" void Bun__queueTaskConcurrently(JSC__JSGlobalObject*, WebCore::EventLoopTask* task);
extern "C" void Bun__performTask(Zig::GlobalObject* globalObject, WebCore::EventLoopTask* task)
{
Expand All @@ -3379,6 +3380,11 @@ void GlobalObject::queueTask(WebCore::EventLoopTask* task)
Bun__queueTask(this, task);
}

void GlobalObject::queueTaskOnTimeout(WebCore::EventLoopTask* task, int timeout)
{
Bun__queueTaskWithTimeout(this, task, timeout);
}

void GlobalObject::queueTaskConcurrently(WebCore::EventLoopTask* task)
{
Bun__queueTaskConcurrently(this, task);
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/ZigGlobalObject.h
Expand Up @@ -149,6 +149,7 @@ class GlobalObject : public JSC::JSGlobalObject {
WebCore::ScriptExecutionContext* scriptExecutionContext() const;

void queueTask(WebCore::EventLoopTask* task);
void queueTaskOnTimeout(WebCore::EventLoopTask* task, int timeout);
void queueTaskConcurrently(WebCore::EventLoopTask* task);

JSDOMStructureMap& structures() WTF_REQUIRES_LOCK(m_gcLock) { return m_structures; }
Expand Down
26 changes: 13 additions & 13 deletions src/bun.js/bindings/webcore/AbortSignal.cpp
Expand Up @@ -59,19 +59,19 @@ Ref<AbortSignal> AbortSignal::abort(JSDOMGlobalObject& globalObject, ScriptExecu
Ref<AbortSignal> AbortSignal::timeout(ScriptExecutionContext& context, uint64_t milliseconds)
{
auto signal = adoptRef(*new AbortSignal(&context));
// signal->setHasActiveTimeoutTimer(true);
// auto action = [signal](ScriptExecutionContext& context) mutable {
// signal->setHasActiveTimeoutTimer(false);

// auto* globalObject = JSC::jsCast<JSDOMGlobalObject*>(context.globalObject());
// if (!globalObject)
// return;

// auto& vm = globalObject->vm();
// Locker locker { vm.apiLock() };
// signal->signalAbort(toJS(globalObject, globalObject, DOMException::create(TimeoutError)));
// };
// DOMTimer::install(context, WTFMove(action), Seconds::fromMilliseconds(milliseconds), true);
signal->setHasActiveTimeoutTimer(true);
auto action = [signal](ScriptExecutionContext& context) mutable {
signal->setHasActiveTimeoutTimer(false);

auto* globalObject = JSC::jsCast<JSDOMGlobalObject*>(context.jsGlobalObject());
if (!globalObject)
return;

auto& vm = globalObject->vm();
Locker locker { vm.apiLock() };
signal->signalAbort(toJS(globalObject, globalObject, DOMException::create(TimeoutError)));
};
context.postTaskOnTimeout(WTFMove(action), Seconds::fromMilliseconds(milliseconds));
return signal;
}

Expand Down
14 changes: 14 additions & 0 deletions src/bun.js/event_loop.zig
Expand Up @@ -566,6 +566,20 @@ pub const EventLoop = struct {
this.tasks.writeItem(task) catch unreachable;
}

pub fn enqueueTaskWithTimeout(this: *EventLoop, task: Task, timeout: i32) void {
// TODO: make this more efficient!
var loop = this.virtual_machine.uws_event_loop orelse @panic("EventLoop.enqueueTaskWithTimeout: uSockets event loop is not initialized");
var timer = uws.Timer.createFallthrough(loop, task.ptr());
timer.set(task.ptr(), callTask, timeout, 0);
}

pub fn callTask(timer: *uws.Timer) callconv(.C) void {
var task = Task.from(timer.as(*anyopaque));
timer.deinit();

JSC.VirtualMachine.vm.enqueueTask(task);
}

pub fn ensureWaker(this: *EventLoop) void {
JSC.markBinding(@src());
if (this.virtual_machine.uws_event_loop == null) {
Expand Down
5 changes: 5 additions & 0 deletions src/bun.js/javascript.zig
Expand Up @@ -280,6 +280,7 @@ comptime {
_ = Bun__onDidAppendPlugin;
_ = Bun__readOriginTimerStart;
_ = Bun__reportUnhandledError;
_ = Bun__queueTaskWithTimeout;
}
}

Expand All @@ -289,6 +290,10 @@ pub export fn Bun__queueTask(global: *JSGlobalObject, task: *JSC.CppTask) void {
global.bunVM().eventLoop().enqueueTask(Task.init(task));
}

pub export fn Bun__queueTaskWithTimeout(global: *JSGlobalObject, task: *JSC.CppTask, milliseconds: i32) void {
global.bunVM().eventLoop().enqueueTaskWithTimeout(Task.init(task), milliseconds);
}

pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JSValue) callconv(.C) JSValue {
var jsc_vm = globalObject.bunVM();
jsc_vm.onUnhandledError(globalObject, value);
Expand Down
12 changes: 12 additions & 0 deletions test/bun.js/abort-signal-timeout.test.js
@@ -0,0 +1,12 @@
import { expect, test } from "bun:test";

test("AbortSignal.timeout", (done) => {
const abort = AbortSignal.timeout(10);
abort.addEventListener("abort", (event) => {
done();
});

// AbortSignal.timeout doesn't keep the event loop / process alive
// so we set a no-op timeout
setTimeout(() => {}, 11);
});

0 comments on commit fe4f39f

Please sign in to comment.