diff --git a/src/bun.js/api/Timer.zig b/src/bun.js/api/Timer.zig index f68932496a..606606ca4f 100644 --- a/src/bun.js/api/Timer.zig +++ b/src/bun.js/api/Timer.zig @@ -26,6 +26,38 @@ pub const TimeoutMap = std.AutoArrayHashMapUnmanaged( const TimerHeap = heap.Intrusive(EventLoopTimer, void, EventLoopTimer.less); +pub const TimerRef = struct { + state: enum { + unset, + ref, + unref, + } = .unset, + + fn setRef(this: *TimerRef, enable: bool, vm: *JSC.VirtualMachine) void { + if (enable and this.state == .ref) { + return; + } else if (!enable and this.state != .ref) { + return; + } + + if (enable) { + this.state = .ref; + vm.timer.incrementTimerRef(1); + } else { + this.state = .unref; + vm.timer.incrementTimerRef(-1); + } + } + + pub fn unref(this: *TimerRef, vm: *JSC.VirtualMachine) void { + setRef(this, false, vm); + } + + pub fn ref(this: *TimerRef, vm: *JSC.VirtualMachine) void { + setRef(this, true, vm); + } +}; + pub const All = struct { last_id: i32 = 1, lock: bun.Mutex = .{}, @@ -173,9 +205,6 @@ pub const All = struct { } pub fn getTimeout(this: *All, spec: *timespec, vm: *VirtualMachine) bool { - if (this.active_timer_count == 0) { - return false; - } if (vm.event_loop.immediate_tasks.count > 0 or vm.event_loop.next_immediate_tasks.count > 0) { spec.* = .{ .nsec = 0, .sec = 0 }; return true; diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index eb86300797..39dfca22da 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -1774,6 +1774,7 @@ pub const DNSResolver = struct { vm: *JSC.VirtualMachine, polls: PollsMap, options: c_ares.ChannelOptions = .{}, + timer_ref: JSC.BunTimer.TimerRef = .{}, ref_count: u32 = 1, event_loop_timer: EventLoopTimer = .{ @@ -1889,8 +1890,8 @@ pub const DNSResolver = struct { const NameInfoPendingCache = bun.HiveArray(GetNameInfoRequest.PendingCacheKey, 32); pub fn checkTimeouts(this: *DNSResolver, now: *const timespec, vm: *JSC.VirtualMachine) EventLoopTimer.Arm { + this.timer_ref.unref(vm); defer { - vm.timer.incrementTimerRef(-1); this.deref(); } @@ -1933,25 +1934,27 @@ pub const DNSResolver = struct { } fn addTimer(this: *DNSResolver, now: ?*const timespec) bool { + this.timer_ref.ref(this.vm); + if (this.event_loop_timer.state == .ACTIVE) { return false; } this.ref(); this.event_loop_timer.next = (now orelse ×pec.now()).addMs(1000); - this.vm.timer.incrementTimerRef(1); this.vm.timer.insert(&this.event_loop_timer); return true; } fn removeTimer(this: *DNSResolver) void { + this.timer_ref.unref(this.vm); + if (this.event_loop_timer.state != .ACTIVE) { return; } // Normally checkTimeouts does this, so we have to be sure to do it ourself if we cancel the timer defer { - this.vm.timer.incrementTimerRef(-1); this.deref(); } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 549d832d2d..d8d741124f 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -103,11 +103,13 @@ pub const TestRunner = struct { unhandled_errors_between_tests: u32 = 0, + test_timeout_ref: JSC.BunTimer.TimerRef = .{}, + pub const Drainer = JSC.AnyTask.New(TestRunner, drain); pub fn onTestTimeout(this: *TestRunner, now: *const bun.timespec, vm: *VirtualMachine) void { - _ = vm; // autofix this.event_loop_timer.state = .FIRED; + this.test_timeout_ref.unref(vm); if (this.pending_test) |pending_test| { if (!pending_test.reported and (this.active_test_for_timeout orelse return) == pending_test.test_id) { @@ -132,6 +134,8 @@ pub const TestRunner = struct { const then = bun.timespec.msFromNow(@intCast(milliseconds)); const vm = JSC.VirtualMachine.get(); + this.test_timeout_ref.ref(vm); + this.event_loop_timer.tag = .TestRunner; if (this.event_loop_timer.state == .ACTIVE) { vm.timer.remove(&this.event_loop_timer);