aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api/bun.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/api/bun.zig')
-rw-r--r--src/bun.js/api/bun.zig155
1 files changed, 131 insertions, 24 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 5b965e969..da4e2945b 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2990,13 +2990,123 @@ pub const Timer = struct {
id: i32 = -1,
kind: Timeout.Kind = .setTimeout,
ref_count: u16 = 1,
+ interval: i32 = 0,
+ // we do not allow the timer to be refreshed after we call clearInterval/clearTimeout
+ has_cleaned_up: bool = false,
pub usingnamespace JSC.Codegen.JSTimeout;
+ pub fn init(globalThis: *JSGlobalObject, id: i32, kind: Timeout.Kind, interval: i32, callback: JSValue, arguments: JSValue) JSValue {
+ var timer = globalThis.allocator().create(TimerObject) catch unreachable;
+ timer.* = .{
+ .id = id,
+ .kind = kind,
+ .interval = interval,
+ };
+ var timer_js = timer.toJS(globalThis);
+ timer_js.ensureStillAlive();
+ TimerObject.argumentsSetCached(timer_js, globalThis, arguments);
+ TimerObject.callbackSetCached(timer_js, globalThis, callback);
+ timer_js.ensureStillAlive();
+ return timer_js;
+ }
+
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 doRefresh(this: *TimerObject, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ // TODO: this is not the optimal way to do this but it works, we should revisit this and optimize it
+ // like truly resetting the timer instead of removing and re-adding when possible
+ const this_value = callframe.this();
+
+ // setImmediate does not support refreshing and we do not support refreshing after cleanup
+ if (this.has_cleaned_up or this.id == -1 or this.kind == .setImmediate) {
+ return JSValue.jsUndefined();
+ }
+ const vm = globalThis.bunVM();
+ var map = vm.timer.maps.get(this.kind);
+
+ // reschedule the event
+ if (TimerObject.callbackGetCached(this_value)) |callback| {
+ callback.ensureStillAlive();
+
+ const id: Timeout.ID = .{
+ .id = this.id,
+ .kind = this.kind,
+ };
+
+ if (this.kind == .setTimeout and this.interval == 0) {
+ var cb: CallbackJob = .{
+ .callback = JSC.Strong.create(callback, globalThis),
+ .globalThis = globalThis,
+ .id = this.id,
+ .kind = this.kind,
+ };
+
+ if (TimerObject.argumentsGetCached(this_value)) |arguments| {
+ arguments.ensureStillAlive();
+ cb.arguments = JSC.Strong.create(arguments, globalThis);
+ }
+
+ var job = vm.allocator.create(CallbackJob) catch @panic(
+ "Out of memory while allocating Timeout",
+ );
+
+ job.* = cb;
+ job.task = CallbackJob.Task.init(job);
+ job.ref.ref(vm);
+
+ // cancel the current event if exists before re-adding it
+ if (map.fetchSwapRemove(this.id)) |timer| {
+ if (timer.value != null) {
+ var value = timer.value.?;
+ value.deinit();
+ }
+ }
+
+ vm.enqueueTask(JSC.Task.init(&job.task));
+
+ map.put(vm.allocator, this.id, null) catch unreachable;
+ return this_value;
+ }
+
+ var timeout = Timeout{
+ .callback = JSC.Strong.create(callback, globalThis),
+ .globalThis = globalThis,
+ .timer = uws.Timer.create(
+ vm.uws_event_loop.?,
+ id,
+ ),
+ };
+
+ if (TimerObject.argumentsGetCached(this_value)) |arguments| {
+ arguments.ensureStillAlive();
+ timeout.arguments = JSC.Strong.create(arguments, globalThis);
+ }
+
+ timeout.poll_ref.ref(vm);
+
+ // cancel the current event if exists before re-adding it
+ if (map.fetchSwapRemove(this.id)) |timer| {
+ if (timer.value != null) {
+ var value = timer.value.?;
+ value.deinit();
+ }
+ }
+
+ map.put(vm.allocator, this.id, timeout) catch unreachable;
+ timeout.timer.set(
+ id,
+ Timeout.run,
+ this.interval,
+ @as(i32, @boolToInt(this.kind == .setInterval)) * this.interval,
+ );
+ return this_value;
+ }
return JSValue.jsUndefined();
}
@@ -3022,6 +3132,10 @@ pub const Timer = struct {
return JSValue.jsNumber(this.id);
}
+ pub fn markHasClear(this: *TimerObject) void {
+ this.has_cleaned_up = true;
+ }
+
pub fn finalize(this: *TimerObject) callconv(.C) void {
bun.default_allocator.destroy(this);
}
@@ -3143,20 +3257,13 @@ pub const Timer = struct {
id: i32,
globalThis: *JSGlobalObject,
callback: JSValue,
- countdown: JSValue,
+ interval: i32,
arguments_array_or_zero: JSValue,
repeat: bool,
) !void {
JSC.markBinding(@src());
var vm = globalThis.bunVM();
- // We don't deal with nesting levels directly
- // but we do set the minimum timeout to be 1ms for repeating timers
- const interval: i32 = @max(
- countdown.coerce(i32, globalThis),
- if (repeat) @as(i32, 1) else 0,
- );
-
const kind: Timeout.Kind = if (repeat) .setInterval else .setTimeout;
var map = vm.timer.maps.get(kind);
@@ -3228,16 +3335,15 @@ pub const Timer = struct {
const id = globalThis.bunVM().timer.last_id;
globalThis.bunVM().timer.last_id +%= 1;
- Timer.set(id, globalThis, callback, countdown, arguments, false) catch
- return JSValue.jsUndefined();
+ const interval: i32 = @max(
+ countdown.coerce(i32, globalThis),
+ 0,
+ );
- var timer = globalThis.allocator().create(TimerObject) catch unreachable;
- timer.* = .{
- .id = id,
- .kind = .setTimeout,
- };
+ Timer.set(id, globalThis, callback, interval, arguments, false) catch
+ return JSValue.jsUndefined();
- return timer.toJS(globalThis);
+ return TimerObject.init(globalThis, id, .setTimeout, interval, callback, arguments);
}
pub fn setInterval(
globalThis: *JSGlobalObject,
@@ -3249,16 +3355,16 @@ pub const Timer = struct {
const id = globalThis.bunVM().timer.last_id;
globalThis.bunVM().timer.last_id +%= 1;
- Timer.set(id, globalThis, callback, countdown, arguments, true) catch
+ // We don't deal with nesting levels directly
+ // but we do set the minimum timeout to be 1ms for repeating timers
+ const interval: i32 = @max(
+ countdown.coerce(i32, globalThis),
+ 1,
+ );
+ Timer.set(id, globalThis, callback, interval, arguments, true) catch
return JSValue.jsUndefined();
- var timer = globalThis.allocator().create(TimerObject) catch unreachable;
- timer.* = .{
- .id = id,
- .kind = .setInterval,
- };
-
- return timer.toJS(globalThis);
+ return TimerObject.init(globalThis, id, .setInterval, interval, callback, arguments);
}
pub fn clearTimer(timer_id_value: JSValue, globalThis: *JSGlobalObject, repeats: bool) void {
@@ -3275,6 +3381,7 @@ pub const Timer = struct {
}
if (TimerObject.fromJS(timer_id_value)) |timer_obj| {
+ timer_obj.markHasClear();
break :brk timer_obj.id;
}