diff options
Diffstat (limited to 'src/bun.js/base.zig')
-rw-r--r-- | src/bun.js/base.zig | 84 |
1 files changed, 76 insertions, 8 deletions
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index c59d3111f..a6df36c4f 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -1706,6 +1706,7 @@ pub const FilePoll = struct { /// on macOS kevent64 has an extra pointer field so we use it for that /// linux doesn't have a field like that generation_number: KQueueGenerationNumber = 0, + next_to_free: ?*FilePoll = null, const FileReader = JSC.WebCore.FileReader; const FileSink = JSC.WebCore.FileSink; @@ -1789,20 +1790,21 @@ pub const FilePoll = struct { this.deinitWithVM(vm); } - pub fn deinitWithoutVM(this: *FilePoll, loop: *uws.Loop, polls: *JSC.FilePoll.HiveArray) void { + fn deinitPossiblyDefer(this: *FilePoll, vm: *JSC.VirtualMachine, loop: *uws.Loop, polls: *JSC.FilePoll.Store) void { if (this.isRegistered()) { _ = this.unregister(loop); } this.owner = Deactivated.owner; + const was_ever_registered = this.flags.contains(.was_ever_registered); this.flags = Flags.Set{}; this.fd = invalid_fd; - polls.put(this); + polls.put(this, vm, was_ever_registered); } pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void { var loop = vm.event_loop_handle.?; - this.deinitWithoutVM(loop, vm.rareData().filePolls(vm)); + this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm)); } pub fn isRegistered(this: *const FilePoll) bool { @@ -1888,6 +1890,9 @@ pub const FilePoll = struct { nonblocking, + was_ever_registered, + ignore_updates, + pub fn poll(this: Flags) Flags { return switch (this) { .readable => .poll_readable, @@ -1949,7 +1954,64 @@ pub const FilePoll = struct { } }; - pub const HiveArray = bun.HiveArray(FilePoll, 128).Fallback; + const HiveArray = bun.HiveArray(FilePoll, 128).Fallback; + + // We defer freeing FilePoll until the end of the next event loop iteration + // This ensures that we don't free a FilePoll before the next callback is called + pub const Store = struct { + hive: HiveArray, + pending_free_head: ?*FilePoll = null, + pending_free_tail: ?*FilePoll = null, + + const log = Output.scoped(.FilePoll, false); + + pub fn init(allocator: std.mem.Allocator) Store { + return .{ + .hive = HiveArray.init(allocator), + }; + } + + pub fn get(this: *Store) *FilePoll { + return this.hive.get(); + } + + pub fn processDeferredFrees(this: *Store) void { + var next = this.pending_free_head; + while (next) |current| { + next = current.next_to_free; + current.next_to_free = null; + this.hive.put(current); + } + this.pending_free_head = null; + this.pending_free_tail = null; + } + + pub fn put(this: *Store, poll: *FilePoll, vm: *JSC.VirtualMachine, ever_registered: bool) void { + if (!ever_registered) { + this.hive.put(poll); + return; + } + + std.debug.assert(poll.next_to_free == null); + + if (this.pending_free_tail) |tail| { + std.debug.assert(this.pending_free_head != null); + std.debug.assert(tail.next_to_free == null); + tail.next_to_free = poll; + } + + if (this.pending_free_head == null) { + this.pending_free_head = poll; + std.debug.assert(this.pending_free_tail == null); + } + + poll.flags.insert(.ignore_updates); + this.pending_free_tail = poll; + std.debug.assert(vm.after_event_loop_callback == null or vm.after_event_loop_callback == @as(?JSC.OpaqueCallback, @ptrCast(&processDeferredFrees))); + vm.after_event_loop_callback = @ptrCast(&processDeferredFrees); + vm.after_event_loop_callback_ctx = this; + } + }; const log = Output.scoped(.FilePoll, false); @@ -1999,7 +2061,6 @@ pub const FilePoll = struct { pub fn activate(this: *FilePoll, loop: *uws.Loop) void { loop.num_polls += @as(i32, @intFromBool(!this.flags.contains(.has_incremented_poll_count))); loop.active += @as(u32, @intFromBool(!this.flags.contains(.disable) and !this.flags.contains(.has_incremented_poll_count))); - this.flags.insert(.has_incremented_poll_count); } @@ -2012,6 +2073,8 @@ pub const FilePoll = struct { poll.fd = @intCast(fd); poll.flags = Flags.Set.init(flags); poll.owner = owner; + poll.next_to_free = null; + if (KQueueGenerationNumber != u0) { max_generation_number +%= 1; poll.generation_number = max_generation_number; @@ -2052,7 +2115,11 @@ pub const FilePoll = struct { if (tag.tag() != @field(Pollable.Tag, "FilePoll")) return; - var file_poll = tag.as(FilePoll); + var file_poll: *FilePoll = tag.as(FilePoll); + if (file_poll.flags.contains(.ignore_updates)) { + return; + } + if (comptime Environment.isMac) onKQueueEvent(file_poll, loop, &loop.ready_polls[@as(usize, @intCast(loop.current_ready_poll))]) else if (comptime Environment.isLinux) @@ -2107,7 +2174,7 @@ pub const FilePoll = struct { @as(std.os.fd_t, @intCast(fd)), &event, ); - + this.flags.insert(.was_ever_registered); if (JSC.Maybe(void).errnoSys(ctl, .epoll_ctl)) |errno| { return errno; } @@ -2180,6 +2247,8 @@ pub const FilePoll = struct { } }; + this.flags.insert(.was_ever_registered); + // If an error occurs while // processing an element of the changelist and there is enough room // in the eventlist, then the event will be placed in the eventlist @@ -2349,7 +2418,6 @@ pub const FilePoll = struct { this.flags.remove(.poll_writable); this.flags.remove(.poll_process); this.flags.remove(.poll_machport); - if (this.isActive()) this.deactivate(loop); |