aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-09-15 01:39:42 -0700
committerGravatar GitHub <noreply@github.com> 2023-09-15 01:39:42 -0700
commitd26addeca147e076d7a5e2c7fe14febdd658393f (patch)
tree65e9669db623ea9201b0dc5176a18925fad6c08b /src
parentf84fbd6e3eafe298be4a88685a5edbc1724753fd (diff)
downloadbun-d26addeca147e076d7a5e2c7fe14febdd658393f.tar.gz
bun-d26addeca147e076d7a5e2c7fe14febdd658393f.tar.zst
bun-d26addeca147e076d7a5e2c7fe14febdd658393f.zip
dup and close file descriptors (#5341)
* track one shot fds * dup fd * skip for rearm on mac * dup if fd * cleanup * force unregister on close * deinitForceUnregister * test * add prompts --------- Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/base.zig31
-rw-r--r--src/bun.js/webcore/streams.zig45
2 files changed, 41 insertions, 35 deletions
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index a6df36c4f..26d5d74c3 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -1787,12 +1787,19 @@ pub const FilePoll = struct {
pub fn deinit(this: *FilePoll) void {
var vm = JSC.VirtualMachine.get();
- this.deinitWithVM(vm);
+ var loop = vm.event_loop_handle.?;
+ this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), false);
+ }
+
+ pub fn deinitForceUnregister(this: *FilePoll) void {
+ var vm = JSC.VirtualMachine.get();
+ var loop = vm.event_loop_handle.?;
+ this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), true);
}
- fn deinitPossiblyDefer(this: *FilePoll, vm: *JSC.VirtualMachine, loop: *uws.Loop, polls: *JSC.FilePoll.Store) void {
+ fn deinitPossiblyDefer(this: *FilePoll, vm: *JSC.VirtualMachine, loop: *uws.Loop, polls: *JSC.FilePoll.Store, force_unregister: bool) void {
if (this.isRegistered()) {
- _ = this.unregister(loop);
+ _ = this.unregister(loop, force_unregister);
}
this.owner = Deactivated.owner;
@@ -1804,7 +1811,7 @@ pub const FilePoll = struct {
pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void {
var loop = vm.event_loop_handle.?;
- this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm));
+ this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), false);
}
pub fn isRegistered(this: *const FilePoll) bool {
@@ -2168,10 +2175,12 @@ pub const FilePoll = struct {
var event = linux.epoll_event{ .events = flags, .data = .{ .u64 = @intFromPtr(Pollable.init(this).ptr()) } };
+ var op: u32 = if (this.isRegistered() or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD;
+
const ctl = linux.epoll_ctl(
watcher_fd,
- if (this.isRegistered() or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD,
- @as(std.os.fd_t, @intCast(fd)),
+ op,
+ @intCast(fd),
&event,
);
this.flags.insert(.was_ever_registered);
@@ -2285,11 +2294,11 @@ pub const FilePoll = struct {
const invalid_fd = bun.invalid_fd;
- pub fn unregister(this: *FilePoll, loop: *uws.Loop) JSC.Maybe(void) {
- return this.unregisterWithFd(loop, this.fd);
+ pub fn unregister(this: *FilePoll, loop: *uws.Loop, force_unregister: bool) JSC.Maybe(void) {
+ return this.unregisterWithFd(loop, this.fd, force_unregister);
}
- pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: bun.UFileDescriptor) JSC.Maybe(void) {
+ pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: bun.UFileDescriptor, force_unregister: bool) JSC.Maybe(void) {
if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport))) {
// no-op
return JSC.Maybe(void).success;
@@ -2310,7 +2319,7 @@ pub const FilePoll = struct {
return JSC.Maybe(void).success;
};
- if (this.flags.contains(.needs_rearm)) {
+ if (this.flags.contains(.needs_rearm) and !force_unregister) {
log("unregister: {s} ({d}) skipped due to needs_rearm", .{ @tagName(flag), fd });
this.flags.remove(.poll_process);
this.flags.remove(.poll_readable);
@@ -2325,7 +2334,7 @@ pub const FilePoll = struct {
const ctl = linux.epoll_ctl(
watcher_fd,
linux.EPOLL.CTL_DEL,
- @as(std.os.fd_t, @intCast(fd)),
+ @intCast(fd),
null,
);
diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig
index a578313d7..b96721e6e 100644
--- a/src/bun.js/webcore/streams.zig
+++ b/src/bun.js/webcore/streams.zig
@@ -3788,7 +3788,6 @@ pub const FIFO = struct {
},
signal: JSC.WebCore.Signal = .{},
is_first_read: bool = true,
- auto_close: bool = true,
has_adjusted_pipe_size_on_linux: bool = false,
drained: bool = true,
@@ -3807,7 +3806,12 @@ pub const FIFO = struct {
pub fn close(this: *FIFO) void {
if (this.poll_ref) |poll| {
this.poll_ref = null;
- poll.deinit();
+ if (comptime Environment.isLinux) {
+ // force target fd to be removed from epoll
+ poll.deinitForceUnregister();
+ } else {
+ poll.deinit();
+ }
}
const fd = this.fd;
@@ -3815,8 +3819,7 @@ pub const FIFO = struct {
defer if (signal_close) this.signal.close(null);
if (signal_close) {
this.fd = bun.invalid_fd;
- if (this.auto_close)
- _ = bun.sys.close(fd);
+ _ = bun.sys.close(fd);
}
this.to_read = null;
@@ -4198,10 +4201,15 @@ pub const File = struct {
file: *Blob.FileStore,
) StreamStart {
var file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- var auto_close = file.pathlike == .path;
- var fd = if (!auto_close)
- file.pathlike.fd
+ var fd = if (file.pathlike != .path)
+ // We will always need to close the file descriptor.
+ switch (Syscall.dup(@intCast(file.pathlike.fd))) {
+ .result => |_fd| _fd,
+ .err => |err| {
+ return .{ .err = err.withPath(file.pathlike.path.slice()) };
+ },
+ }
else switch (Syscall.open(file.pathlike.path.sliceZ(&file_buf), std.os.O.RDONLY | std.os.O.NONBLOCK | std.os.O.CLOEXEC, 0)) {
.result => |_fd| _fd,
.err => |err| {
@@ -4218,7 +4226,7 @@ pub const File = struct {
}
}
- if (!auto_close and !(file.is_atty orelse false)) {
+ if (file.pathlike != .path and !(file.is_atty orelse false)) {
if (comptime Environment.isWindows) {
bun.todo(@src(), {});
} else {
@@ -4229,7 +4237,6 @@ pub const File = struct {
// if we do not, clone the descriptor and set non-blocking
// it is important for us to clone it so we don't cause Weird Things to happen
if ((flags & std.os.O.NONBLOCK) == 0) {
- auto_close = true;
fd = switch (Syscall.fcntl(fd, std.os.F.DUPFD, 0)) {
.result => |_fd| @as(@TypeOf(fd), @intCast(_fd)),
.err => |err| return .{ .err = err },
@@ -4249,24 +4256,18 @@ pub const File = struct {
const stat: bun.Stat = switch (Syscall.fstat(fd)) {
.result => |result| result,
.err => |err| {
- if (auto_close) {
- _ = Syscall.close(fd);
- }
+ _ = Syscall.close(fd);
return .{ .err = err };
},
};
if (std.os.S.ISDIR(stat.mode)) {
- if (auto_close) {
- _ = Syscall.close(fd);
- }
+ _ = Syscall.close(fd);
return .{ .err = Syscall.Error.fromCode(.ISDIR, .fstat) };
}
if (std.os.S.ISSOCK(stat.mode)) {
- if (auto_close) {
- _ = Syscall.close(fd);
- }
+ _ = Syscall.close(fd);
return .{ .err = Syscall.Error.fromCode(.INVAL, .fstat) };
}
@@ -4292,9 +4293,7 @@ pub const File = struct {
file.max_size = this.remaining_bytes;
if (this.remaining_bytes == 0) {
- if (auto_close) {
- _ = Syscall.close(fd);
- }
+ _ = Syscall.close(fd);
return .{ .empty = {} };
}
@@ -4303,7 +4302,6 @@ pub const File = struct {
}
this.fd = fd;
- this.auto_close = auto_close;
return StreamStart{ .ready = {} };
}
@@ -4725,7 +4723,6 @@ pub const FileReader = struct {
.readable = .{
.FIFO = .{
.fd = readable_file.fd,
- .auto_close = readable_file.auto_close,
.drained = this.buffered_data.len == 0,
},
},
@@ -4885,7 +4882,7 @@ pub fn NewReadyWatcher(
const fd = @as(c_int, @intCast(fd_));
std.debug.assert(@as(c_int, @intCast(this.poll_ref.?.fd)) == fd);
std.debug.assert(
- this.poll_ref.?.unregister(JSC.VirtualMachine.get().event_loop_handle.?) == .result,
+ this.poll_ref.?.unregister(JSC.VirtualMachine.get().event_loop_handle.?, false) == .result,
);
}