aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/javascript.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/javascript.zig')
-rw-r--r--src/javascript/jsc/javascript.zig183
1 files changed, 165 insertions, 18 deletions
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 727ac9654..879e944e3 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -14,6 +14,7 @@ const default_allocator = _global.default_allocator;
const StoredFileDescriptorType = _global.StoredFileDescriptorType;
const Arena = @import("../../mimalloc_arena.zig").Arena;
const C = _global.C;
+const NetworkThread = @import("http").NetworkThread;
pub fn zigCast(comptime Destination: type, value: anytype) *Destination {
return @ptrCast(*Destination, @alignCast(@alignOf(*Destination), value));
@@ -52,6 +53,7 @@ const Headers = WebCore.Headers;
const Fetch = WebCore.Fetch;
const FetchEvent = WebCore.FetchEvent;
const js = @import("../../jsc.zig").C;
+const JSC = @import("../../jsc.zig");
const JSError = @import("./base.zig").JSError;
const d = @import("./base.zig").d;
const MarkedArrayBuffer = @import("./base.zig").MarkedArrayBuffer;
@@ -1116,46 +1118,138 @@ pub const Bun = struct {
pub const Timer = struct {
last_id: i32 = 0,
warned: bool = false,
+ active: u32 = 0,
+ timeouts: TimeoutMap = TimeoutMap{},
+
+ const TimeoutMap = std.AutoArrayHashMapUnmanaged(i32, *Timeout);
pub fn getNextID() callconv(.C) i32 {
VirtualMachine.vm.timer.last_id += 1;
return VirtualMachine.vm.timer.last_id;
}
+ pub const Timeout = struct {
+ id: i32 = 0,
+ callback: JSValue,
+ interval: i32 = 0,
+ completion: NetworkThread.Completion = undefined,
+ repeat: bool = false,
+ io_task: ?*TimeoutTask = null,
+ cancelled: bool = false,
+
+ pub const TimeoutTask = IOTask(Timeout);
+
+ pub fn run(this: *Timeout, _task: *TimeoutTask) void {
+ this.io_task = _task;
+ NetworkThread.global.pool.io.?.timeout(
+ *Timeout,
+ this,
+ onCallback,
+ &this.completion,
+ std.time.ns_per_ms * @intCast(
+ u63,
+ @maximum(
+ this.interval,
+ 1,
+ ),
+ ),
+ );
+ }
+
+ pub fn onCallback(this: *Timeout, _: *NetworkThread.Completion, _: NetworkThread.AsyncIO.TimeoutError!void) void {
+ this.io_task.?.onFinish();
+ }
+
+ pub fn then(this: *Timeout, global: *JSGlobalObject) void {
+ if (!this.cancelled) {
+ if (this.repeat) {
+ this.io_task.?.deinit();
+ var task = Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, global, this) catch unreachable;
+ this.io_task = task;
+ task.schedule();
+ }
+
+ _ = JSC.C.JSObjectCallAsFunction(global.ref(), this.callback.asObjectRef(), null, 0, null, null);
+
+ if (this.repeat)
+ return;
+ }
+
+ this.clear(global);
+ }
+
+ pub fn clear(this: *Timeout, global: *JSGlobalObject) void {
+ this.cancelled = true;
+ JSC.C.JSValueUnprotect(global.ref(), this.callback.asObjectRef());
+ _ = VirtualMachine.vm.timer.timeouts.swapRemove(this.id);
+ if (this.io_task) |task| {
+ task.deinit();
+ }
+ VirtualMachine.vm.allocator.destroy(this);
+ VirtualMachine.vm.timer.active -|= 1;
+ }
+ };
+
+ fn set(
+ id: i32,
+ globalThis: *JSGlobalObject,
+ callback: JSValue,
+ countdown: JSValue,
+ repeat: bool,
+ ) !void {
+ var timeout = try VirtualMachine.vm.allocator.create(Timeout);
+ js.JSValueProtect(globalThis.ref(), callback.asObjectRef());
+ timeout.* = Timeout{ .id = id, .callback = callback, .interval = countdown.toInt32(), .repeat = repeat };
+ var task = try Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, globalThis, timeout);
+ VirtualMachine.vm.timer.timeouts.put(VirtualMachine.vm.allocator, id, timeout) catch unreachable;
+ VirtualMachine.vm.timer.active +|= 1;
+ task.schedule();
+ }
+
pub fn setTimeout(
- _: *JSGlobalObject,
- _: JSValue,
- _: JSValue,
+ globalThis: *JSGlobalObject,
+ callback: JSValue,
+ countdown: JSValue,
) callconv(.C) JSValue {
- VirtualMachine.vm.timer.last_id += 1;
+ const id = VirtualMachine.vm.timer.last_id;
+ VirtualMachine.vm.timer.last_id +%= 1;
- Output.prettyWarnln("setTimeout is not implemented yet", .{});
+ Timer.set(id, globalThis, callback, countdown, false) catch
+ return JSValue.jsUndefined();
- // For now, we are going to straight up lie
- return JSValue.jsNumber(@intCast(i32, VirtualMachine.vm.timer.last_id));
+ return JSValue.jsNumber(@intCast(i32, id));
}
pub fn setInterval(
- _: *JSGlobalObject,
- _: JSValue,
- _: JSValue,
+ globalThis: *JSGlobalObject,
+ callback: JSValue,
+ countdown: JSValue,
) callconv(.C) JSValue {
- VirtualMachine.vm.timer.last_id += 1;
+ const id = VirtualMachine.vm.timer.last_id;
+ VirtualMachine.vm.timer.last_id +%= 1;
+
+ Timer.set(id, globalThis, callback, countdown, true) catch
+ return JSValue.jsUndefined();
- Output.prettyWarnln("setInterval is not implemented yet", .{});
+ return JSValue.jsNumber(@intCast(i32, id));
+ }
- // For now, we are going to straight up lie
- return JSValue.jsNumber(@intCast(i32, VirtualMachine.vm.timer.last_id));
+ pub fn clearTimer(id: JSValue, _: *JSGlobalObject) void {
+ var timer: *Timeout = VirtualMachine.vm.timer.timeouts.get(id.toInt32()) orelse return;
+ timer.cancelled = true;
}
+
pub fn clearTimeout(
- _: *JSGlobalObject,
- _: JSValue,
+ globalThis: *JSGlobalObject,
+ id: JSValue,
) callconv(.C) JSValue {
+ Timer.clearTimer(id, globalThis);
return JSValue.jsUndefined();
}
pub fn clearInterval(
- _: *JSGlobalObject,
- _: JSValue,
+ globalThis: *JSGlobalObject,
+ id: JSValue,
) callconv(.C) JSValue {
+ Timer.clearTimer(id, globalThis);
return JSValue.jsUndefined();
}
@@ -1466,12 +1560,60 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type {
}
};
}
+
+pub fn IOTask(comptime Context: type) type {
+ return struct {
+ const This = @This();
+ ctx: *Context,
+ task: NetworkThread.Task = .{ .callback = runFromThreadPool },
+ event_loop: *VirtualMachine.EventLoop,
+ allocator: std.mem.Allocator,
+ globalThis: *JSGlobalObject,
+
+ pub fn createOnJSThread(allocator: std.mem.Allocator, globalThis: *JSGlobalObject, value: *Context) !*This {
+ var this = try allocator.create(This);
+ this.* = .{
+ .event_loop = VirtualMachine.vm.event_loop,
+ .ctx = value,
+ .allocator = allocator,
+ .globalThis = globalThis,
+ };
+ return this;
+ }
+
+ pub fn runFromThreadPool(task: *NetworkThread.Task) void {
+ var this = @fieldParentPtr(This, "task", task);
+ Context.run(this.ctx, this);
+ }
+
+ pub fn runFromJS(this: This) void {
+ var ctx = this.ctx;
+ ctx.then(this.globalThis);
+ }
+
+ pub fn schedule(this: *This) void {
+ NetworkThread.init() catch return;
+ NetworkThread.global.pool.schedule(NetworkThread.Batch.from(&this.task));
+ }
+
+ pub fn onFinish(this: *This) void {
+ this.event_loop.enqueueTaskConcurrent(Task.init(this));
+ }
+
+ pub fn deinit(this: *This) void {
+ this.allocator.destroy(this);
+ }
+ };
+}
+
const AsyncTransformTask = @import("./api/transpiler.zig").TransformTask.AsyncTransformTask;
+const BunTimerTimeoutTask = Bun.Timer.Timeout.TimeoutTask;
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
pub const Task = TaggedPointerUnion(.{
FetchTasklet,
Microtask,
AsyncTransformTask,
+ BunTimerTimeoutTask,
// PromiseTask,
// TimeoutTasklet,
});
@@ -1558,6 +1700,11 @@ pub const VirtualMachine = struct {
transform_task.deinit();
finished += 1;
},
+ @field(Task.Tag, @typeName(BunTimerTimeoutTask)) => {
+ var transform_task: *BunTimerTimeoutTask = task.get(BunTimerTimeoutTask).?;
+ transform_task.*.runFromJS();
+ finished += 1;
+ },
else => unreachable,
}
}