aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/api')
-rw-r--r--src/bun.js/api/bun.zig142
1 files changed, 111 insertions, 31 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 4dcbad374..78927f85a 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2777,12 +2777,19 @@ pub const Timer = struct {
warned: bool = false,
// We split up the map here to avoid storing an extra "repeat" boolean
-
- /// Used by setTimeout()
- timeout_map: TimeoutMap = TimeoutMap{},
-
- /// Used by setInterval()
- interval_map: TimeoutMap = TimeoutMap{},
+ maps: struct {
+ setTimeout: TimeoutMap = .{},
+ setInterval: TimeoutMap = .{},
+ setImmediate: TimeoutMap = .{},
+
+ pub inline fn get(this: *@This(), kind: Timeout.Kind) *TimeoutMap {
+ return switch (kind) {
+ .setTimeout => &this.setTimeout,
+ .setInterval => &this.setInterval,
+ .setImmediate => &this.setImmediate,
+ };
+ }
+ } = .{},
/// TimeoutMap is map of i32 to nullable Timeout structs
/// i32 is exposed to JavaScript and can be used with clearTimeout, clearInterval, etc.
@@ -2812,7 +2819,7 @@ pub const Timer = struct {
globalThis: *JSC.JSGlobalObject,
callback: JSC.Strong = .{},
arguments: JSC.Strong = .{},
- repeat: bool = false,
+ kind: Timeout.Kind = .setTimeout,
pub const Task = JSC.AnyTask.New(CallbackJob, perform);
@@ -2849,12 +2856,13 @@ pub const Timer = struct {
pub fn perform(this: *CallbackJob) void {
var globalThis = this.globalThis;
var vm = globalThis.bunVM();
- var map: *TimeoutMap = if (this.repeat) &vm.timer.interval_map else &vm.timer.timeout_map;
+ const kind = this.kind;
+ var map: *TimeoutMap = vm.timer.maps.get(kind);
// This doesn't deinit the timer
// Timers are deinit'd separately
// We do need to handle when the timer is cancelled after the job has been enqueued
- if (!this.repeat) {
+ if (kind != .setInterval) {
if (map.fetchSwapRemove(this.id) == null) {
// if the timeout was cancelled, don't run the callback
this.deinit();
@@ -2934,6 +2942,47 @@ pub const Timer = struct {
}
};
+ pub const TimerObject = struct {
+ id: i32 = -1,
+ kind: Timeout.Kind = .setTimeout,
+ ref_count: u16 = 1,
+
+ pub usingnamespace JSC.Codegen.JSTimeout;
+
+ pub fn doRef(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ if (this.ref_count > 0)
+ this.ref_count +|= 1;
+
+ return JSValue.jsUndefined();
+ }
+
+ pub fn doUnref(this: *TimerObject, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ this.ref_count -|= 1;
+ if (this.ref_count == 0) {
+ switch (this.kind) {
+ .setTimeout, .setImmediate => {
+ _ = clearTimeout(globalObject, JSValue.jsNumber(this.id));
+ },
+ .setInterval => {
+ _ = clearInterval(globalObject, JSValue.jsNumber(this.id));
+ },
+ }
+ }
+
+ return JSValue.jsUndefined();
+ }
+ pub fn hasRef(this: *TimerObject, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ return JSValue.jsBoolean(this.ref_count > 0 and globalObject.bunVM().timer.maps.get(this.kind).contains(this.id));
+ }
+ pub fn toPrimitive(this: *TimerObject, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ return JSValue.jsNumber(this.id);
+ }
+
+ pub fn finalize(this: *TimerObject) callconv(.C) void {
+ bun.default_allocator.destroy(this);
+ }
+ };
+
pub const Timeout = struct {
callback: JSC.Strong = .{},
globalThis: *JSC.JSGlobalObject,
@@ -2941,11 +2990,21 @@ pub const Timer = struct {
poll_ref: JSC.PollRef = JSC.PollRef.init(),
arguments: JSC.Strong = .{},
+ pub const Kind = enum(u32) {
+ setTimeout,
+ setInterval,
+ setImmediate,
+ };
+
// this is sized to be the same as one pointer
pub const ID = extern struct {
id: i32,
- repeat: u32 = 0,
+ kind: Kind = Kind.setTimeout,
+
+ pub fn repeats(this: ID) bool {
+ return this.kind == .setInterval;
+ }
};
pub fn run(timer: *uws.Timer) callconv(.C) void {
@@ -2955,9 +3014,9 @@ pub const Timer = struct {
// to handle the timeout being cancelled after already enqueued
var vm = JSC.VirtualMachine.get();
- const repeats = timer_id.repeat > 0;
+ const repeats = timer_id.repeats();
- var map = if (repeats) &vm.timer.interval_map else &vm.timer.timeout_map;
+ var map = vm.timer.maps.get(timer_id.kind);
var this_: ?Timeout = map.get(
timer_id.id,
@@ -3000,7 +3059,7 @@ pub const Timer = struct {
this.arguments,
.globalThis = globalThis,
.id = timer_id.id,
- .repeat = timer_id.repeat > 0,
+ .kind = timer_id.kind,
};
// This allows us to:
@@ -3054,19 +3113,18 @@ pub const Timer = struct {
if (repeat) @as(i32, 1) else 0,
);
- var map = if (repeat)
- &vm.timer.interval_map
- else
- &vm.timer.timeout_map;
+ const kind: Timeout.Kind = if (repeat) .setInterval else .setTimeout;
+
+ var map = vm.timer.maps.get(kind);
// setImmediate(foo)
// setTimeout(foo, 0)
- if (interval == 0) {
+ if (kind == .setTimeout and interval == 0) {
var cb: CallbackJob = .{
.callback = JSC.Strong.create(callback, globalThis),
.globalThis = globalThis,
.id = id,
- .repeat = false,
+ .kind = kind,
};
if (arguments_array_or_zero != .zero) {
@@ -3093,7 +3151,7 @@ pub const Timer = struct {
vm.uws_event_loop.?,
Timeout.ID{
.id = id,
- .repeat = @as(u32, @boolToInt(repeat)),
+ .kind = kind,
},
),
};
@@ -3108,11 +3166,11 @@ pub const Timer = struct {
timeout.timer.set(
Timeout.ID{
.id = id,
- .repeat = if (repeat) 1 else 0,
+ .kind = kind,
},
Timeout.run,
interval,
- @as(i32, @boolToInt(repeat)) * interval,
+ @as(i32, @boolToInt(kind == .setInterval)) * interval,
);
}
@@ -3129,7 +3187,13 @@ pub const Timer = struct {
Timer.set(id, globalThis, callback, countdown, arguments, false) catch
return JSValue.jsUndefined();
- return JSValue.jsNumberWithType(i32, id);
+ var timer = globalThis.allocator().create(TimerObject) catch unreachable;
+ timer.* = .{
+ .id = id,
+ .kind = .setTimeout,
+ };
+
+ return timer.toJS(globalThis);
}
pub fn setInterval(
globalThis: *JSGlobalObject,
@@ -3144,21 +3208,37 @@ pub const Timer = struct {
Timer.set(id, globalThis, callback, countdown, arguments, true) catch
return JSValue.jsUndefined();
- return JSValue.jsNumberWithType(i32, id);
+ var timer = globalThis.allocator().create(TimerObject) catch unreachable;
+ timer.* = .{
+ .id = id,
+ .kind = .setInterval,
+ };
+
+ return timer.toJS(globalThis);
}
- pub fn clearTimer(timer_id: JSValue, globalThis: *JSGlobalObject, repeats: bool) void {
+ pub fn clearTimer(timer_id_value: JSValue, globalThis: *JSGlobalObject, repeats: bool) void {
JSC.markBinding(@src());
- var map = if (repeats) &VirtualMachine.get().timer.interval_map else &VirtualMachine.get().timer.timeout_map;
- if (!timer_id.isAnyInt()) {
- return;
- }
+ const kind: Timeout.Kind = if (repeats) .setInterval else .setTimeout;
+
+ var map = globalThis.bunVM().timer.maps.get(kind);
const id: Timeout.ID = .{
- .id = timer_id.coerce(i32, globalThis),
- .repeat = @as(u32, @boolToInt(repeats)),
+ .id = brk: {
+ if (timer_id_value.isAnyInt()) {
+ break :brk timer_id_value.coerce(i32, globalThis);
+ }
+
+ if (TimerObject.fromJS(timer_id_value)) |timer_obj| {
+ break :brk timer_obj.id;
+ }
+
+ return;
+ },
+ .kind = kind,
};
+
var timer = map.fetchSwapRemove(id.id) orelse return;
if (timer.value == null) {
// this timer was scheduled to run but was cancelled before it was run