mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Compare commits
10 Commits
ci-codesig
...
nektro-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4955eb0b2a | ||
|
|
b9fc52bf76 | ||
|
|
e331fea16a | ||
|
|
a116bf9e7b | ||
|
|
714324e891 | ||
|
|
000ea07cbe | ||
|
|
5e5ecd14f2 | ||
|
|
8219f155b6 | ||
|
|
d6757df1a6 | ||
|
|
66d71fde0e |
1
.vscode/launch.json
generated
vendored
1
.vscode/launch.json
generated
vendored
@@ -151,7 +151,6 @@
|
||||
"env": {
|
||||
"FORCE_COLOR": "0",
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_DEBUG_EventLoop": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
|
||||
},
|
||||
"console": "internalConsole",
|
||||
|
||||
@@ -176,6 +176,7 @@ pub const Subprocess = struct {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
process: *Process,
|
||||
stdin: Writable,
|
||||
stdout: Readable,
|
||||
@@ -202,13 +203,11 @@ pub const Subprocess = struct {
|
||||
ipc_callback: JSC.Strong = .{},
|
||||
flags: Flags = .{},
|
||||
|
||||
weak_file_sink_stdin_ptr: ?*JSC.WebCore.FileSink = null,
|
||||
island: *Island,
|
||||
|
||||
pub const Flags = packed struct {
|
||||
is_sync: bool = false,
|
||||
killed: bool = false,
|
||||
has_stdin_destructor_called: bool = false,
|
||||
finalized: bool = false,
|
||||
};
|
||||
|
||||
pub const SignalCode = bun.SignalCode;
|
||||
@@ -704,19 +703,6 @@ pub const Subprocess = struct {
|
||||
return .undefined;
|
||||
}
|
||||
|
||||
pub fn onStdinDestroyed(this: *Subprocess) void {
|
||||
this.flags.has_stdin_destructor_called = true;
|
||||
this.weak_file_sink_stdin_ptr = null;
|
||||
|
||||
if (this.flags.finalized) {
|
||||
// if the process has already been garbage collected, we can free the memory now
|
||||
bun.default_allocator.destroy(this);
|
||||
} else {
|
||||
// otherwise update the pending activity flag
|
||||
this.updateHasPendingActivity();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSValue {
|
||||
const ipc_data = &(this.ipc_data orelse {
|
||||
if (this.hasExited()) {
|
||||
@@ -1182,13 +1168,6 @@ pub const Subprocess = struct {
|
||||
// When the stream has closed we need to be notified to prevent a use-after-free
|
||||
// We can test for this use-after-free by enabling hot module reloading on a file and then saving it twice
|
||||
pub fn onClose(this: *Writable, _: ?bun.sys.Error) void {
|
||||
const process: *Subprocess = @fieldParentPtr("stdin", this);
|
||||
|
||||
if (process.this_jsvalue != .zero) {
|
||||
if (Subprocess.stdinGetCached(process.this_jsvalue)) |existing_value| {
|
||||
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.*) {
|
||||
.buffer => {
|
||||
@@ -1196,11 +1175,11 @@ pub const Subprocess = struct {
|
||||
},
|
||||
.pipe => {
|
||||
this.pipe.deref();
|
||||
this.pipe.island.?.clearStdin();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
process.onStdinDestroyed();
|
||||
|
||||
this.* = .{
|
||||
.ignore = {},
|
||||
@@ -1212,7 +1191,7 @@ pub const Subprocess = struct {
|
||||
pub fn init(
|
||||
stdio: Stdio,
|
||||
event_loop: *JSC.EventLoop,
|
||||
subprocess: *Subprocess,
|
||||
island: *Subprocess.Island,
|
||||
result: StdioResult,
|
||||
) !Writable {
|
||||
assertStdioResult(result);
|
||||
@@ -1232,8 +1211,8 @@ pub const Subprocess = struct {
|
||||
},
|
||||
}
|
||||
pipe.writer.setParent(pipe);
|
||||
subprocess.weak_file_sink_stdin_ptr = pipe;
|
||||
subprocess.flags.has_stdin_destructor_called = false;
|
||||
pipe.island = island;
|
||||
island.stdin = pipe;
|
||||
|
||||
return Writable{
|
||||
.pipe = pipe,
|
||||
@@ -1244,12 +1223,12 @@ pub const Subprocess = struct {
|
||||
|
||||
.blob => |blob| {
|
||||
return Writable{
|
||||
.buffer = StaticPipeWriter.create(event_loop, subprocess, result, .{ .blob = blob }),
|
||||
.buffer = StaticPipeWriter.create(event_loop, island.subprocess.?, result, .{ .blob = blob }),
|
||||
};
|
||||
},
|
||||
.array_buffer => |array_buffer| {
|
||||
return Writable{
|
||||
.buffer = StaticPipeWriter.create(event_loop, subprocess, result, .{ .array_buffer = array_buffer }),
|
||||
.buffer = StaticPipeWriter.create(event_loop, island.subprocess.?, result, .{ .array_buffer = array_buffer }),
|
||||
};
|
||||
},
|
||||
.fd => |fd| {
|
||||
@@ -1290,8 +1269,7 @@ pub const Subprocess = struct {
|
||||
},
|
||||
}
|
||||
|
||||
subprocess.weak_file_sink_stdin_ptr = pipe;
|
||||
subprocess.flags.has_stdin_destructor_called = false;
|
||||
island.stdin = pipe;
|
||||
|
||||
pipe.writer.handle.poll.flags.insert(.socket);
|
||||
|
||||
@@ -1302,12 +1280,12 @@ pub const Subprocess = struct {
|
||||
|
||||
.blob => |blob| {
|
||||
return Writable{
|
||||
.buffer = StaticPipeWriter.create(event_loop, subprocess, result, .{ .blob = blob }),
|
||||
.buffer = StaticPipeWriter.create(event_loop, island.subprocess.?, result, .{ .blob = blob }),
|
||||
};
|
||||
},
|
||||
.array_buffer => |array_buffer| {
|
||||
return Writable{
|
||||
.buffer = StaticPipeWriter.create(event_loop, subprocess, result, .{ .array_buffer = array_buffer }),
|
||||
.buffer = StaticPipeWriter.create(event_loop, island.subprocess.?, result, .{ .array_buffer = array_buffer }),
|
||||
};
|
||||
},
|
||||
.memfd => |memfd| {
|
||||
@@ -1336,18 +1314,16 @@ pub const Subprocess = struct {
|
||||
.buffer, .inherit => JSValue.jsUndefined(),
|
||||
.pipe => |pipe| {
|
||||
this.* = .{ .ignore = {} };
|
||||
if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) {
|
||||
if (subprocess.process.hasExited() and subprocess.island.stdin != null) {
|
||||
pipe.onAttachedProcessExit();
|
||||
return pipe.toJS(globalThis);
|
||||
} else {
|
||||
subprocess.flags.has_stdin_destructor_called = false;
|
||||
subprocess.weak_file_sink_stdin_ptr = pipe;
|
||||
subprocess.island.stdin = pipe;
|
||||
if (@intFromPtr(pipe.signal.ptr) == @intFromPtr(subprocess)) {
|
||||
pipe.signal.clear();
|
||||
}
|
||||
return pipe.toJSWithDestructor(
|
||||
globalThis,
|
||||
JSC.WebCore.SinkDestructor.Ptr.init(subprocess),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -1355,13 +1331,6 @@ pub const Subprocess = struct {
|
||||
}
|
||||
|
||||
pub fn finalize(this: *Writable) void {
|
||||
const subprocess: *Subprocess = @fieldParentPtr("stdin", this);
|
||||
if (subprocess.this_jsvalue != .zero) {
|
||||
if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| {
|
||||
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return switch (this.*) {
|
||||
.pipe => |pipe| {
|
||||
if (pipe.signal.ptr == @as(*anyopaque, @ptrCast(this))) {
|
||||
@@ -1411,7 +1380,7 @@ pub const Subprocess = struct {
|
||||
this.pid_rusage = rusage.*;
|
||||
const is_sync = this.flags.is_sync;
|
||||
|
||||
var stdin: ?*JSC.WebCore.FileSink = this.weak_file_sink_stdin_ptr;
|
||||
var stdin: ?*JSC.WebCore.FileSink = this.island.stdin;
|
||||
var existing_stdin_value = JSC.JSValue.zero;
|
||||
if (this_jsvalue != .zero) {
|
||||
if (JSC.Codegen.JSSubprocess.stdinGetCached(this_jsvalue)) |existing_value| {
|
||||
@@ -1429,14 +1398,9 @@ pub const Subprocess = struct {
|
||||
this.stdin.buffer.close();
|
||||
}
|
||||
|
||||
if (existing_stdin_value != .zero) {
|
||||
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_stdin_value, 0);
|
||||
}
|
||||
|
||||
if (stdin) |pipe| {
|
||||
this.weak_file_sink_stdin_ptr = null;
|
||||
this.flags.has_stdin_destructor_called = true;
|
||||
pipe.onAttachedProcessExit();
|
||||
_ = pipe;
|
||||
this.island.clearStdin();
|
||||
}
|
||||
|
||||
var did_update_has_pending_activity = false;
|
||||
@@ -1564,11 +1528,8 @@ pub const Subprocess = struct {
|
||||
this.process.detach();
|
||||
this.process.deref();
|
||||
|
||||
this.flags.finalized = true;
|
||||
if (this.weak_file_sink_stdin_ptr == null) {
|
||||
// if no file sink exists we can free immediately
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
this.island.clearSubprocess();
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
|
||||
pub fn getExited(
|
||||
@@ -2075,21 +2036,14 @@ pub const Subprocess = struct {
|
||||
}
|
||||
|
||||
const loop = jsc_vm.eventLoop();
|
||||
var island = Island.new(.{});
|
||||
|
||||
// When run synchronously, subprocess isn't garbage collected
|
||||
subprocess.* = Subprocess{
|
||||
.globalThis = globalThis,
|
||||
.process = spawned.toProcess(loop, is_sync),
|
||||
.pid_rusage = null,
|
||||
.stdin = Writable.init(
|
||||
stdio[0],
|
||||
loop,
|
||||
subprocess,
|
||||
spawned.stdin,
|
||||
) catch {
|
||||
globalThis.throwOutOfMemory();
|
||||
return .zero;
|
||||
},
|
||||
.stdin = undefined,
|
||||
.stdout = Readable.init(
|
||||
stdio[1],
|
||||
loop,
|
||||
@@ -2126,6 +2080,17 @@ pub const Subprocess = struct {
|
||||
.flags = .{
|
||||
.is_sync = is_sync,
|
||||
},
|
||||
.island = island,
|
||||
};
|
||||
island.subprocess = subprocess;
|
||||
subprocess.stdin = Writable.init(
|
||||
stdio[0],
|
||||
loop,
|
||||
island,
|
||||
spawned.stdin,
|
||||
) catch {
|
||||
globalThis.throwOutOfMemory();
|
||||
return .zero;
|
||||
};
|
||||
subprocess.process.setExitHandler(subprocess);
|
||||
|
||||
@@ -2288,4 +2253,21 @@ pub const Subprocess = struct {
|
||||
}
|
||||
|
||||
pub const IPCHandler = IPC.NewIPCHandler(Subprocess);
|
||||
|
||||
pub const Island = struct {
|
||||
pub usingnamespace bun.New(@This());
|
||||
|
||||
subprocess: ?*Subprocess = null,
|
||||
stdin: ?*JSC.WebCore.FileSink = null,
|
||||
|
||||
pub fn clearSubprocess(this: *Island) void {
|
||||
this.subprocess = null;
|
||||
if (this.stdin == null) this.destroy();
|
||||
}
|
||||
|
||||
pub fn clearStdin(this: *Island) void {
|
||||
this.stdin = null;
|
||||
if (this.subprocess == null) this.destroy();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
10
src/bun.js/bindings/headers.h
generated
10
src/bun.js/bindings/headers.h
generated
@@ -596,7 +596,7 @@ extern "C" JSC__JSValue SYSV_ABI Bun__Path__toNamespacedPath(JSC__JSGlobalObject
|
||||
#endif
|
||||
|
||||
CPP_DECL JSC__JSValue ArrayBufferSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
|
||||
CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
|
||||
CPP_DECL JSC__JSValue ArrayBufferSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
|
||||
CPP_DECL void ArrayBufferSink__detachPtr(JSC__JSValue JSValue0);
|
||||
CPP_DECL void* ArrayBufferSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL void ArrayBufferSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
|
||||
@@ -616,7 +616,7 @@ BUN_DECLARE_HOST_FUNCTION(ArrayBufferSink__write);
|
||||
|
||||
#endif
|
||||
CPP_DECL JSC__JSValue HTTPSResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
|
||||
CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
|
||||
CPP_DECL JSC__JSValue HTTPSResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
|
||||
CPP_DECL void HTTPSResponseSink__detachPtr(JSC__JSValue JSValue0);
|
||||
CPP_DECL void* HTTPSResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL void HTTPSResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
|
||||
@@ -636,7 +636,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPSResponseSink__write);
|
||||
|
||||
#endif
|
||||
CPP_DECL JSC__JSValue HTTPResponseSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
|
||||
CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
|
||||
CPP_DECL JSC__JSValue HTTPResponseSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
|
||||
CPP_DECL void HTTPResponseSink__detachPtr(JSC__JSValue JSValue0);
|
||||
CPP_DECL void* HTTPResponseSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL void HTTPResponseSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
|
||||
@@ -656,7 +656,7 @@ BUN_DECLARE_HOST_FUNCTION(HTTPResponseSink__write);
|
||||
|
||||
#endif
|
||||
CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
|
||||
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
|
||||
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
|
||||
CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0);
|
||||
CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
|
||||
@@ -677,7 +677,7 @@ BUN_DECLARE_HOST_FUNCTION(FileSink__write);
|
||||
#endif
|
||||
|
||||
CPP_DECL JSC__JSValue FileSink__assignToStream(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, void* arg2, void** arg3);
|
||||
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1, uintptr_t destructor);
|
||||
CPP_DECL JSC__JSValue FileSink__createObject(JSC__JSGlobalObject* arg0, void* arg1);
|
||||
CPP_DECL void FileSink__detachPtr(JSC__JSValue JSValue0);
|
||||
CPP_DECL void* FileSink__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL void FileSink__onClose(JSC__JSValue JSValue0, JSC__JSValue JSValue1);
|
||||
|
||||
12
src/bun.js/bindings/headers.zig
generated
12
src/bun.js/bindings/headers.zig
generated
@@ -357,30 +357,26 @@ pub extern fn Zig__GlobalObject__create(arg0: ?*anyopaque, arg1: i32, arg2: bool
|
||||
pub extern fn Zig__GlobalObject__getModuleRegistryMap(arg0: *bindings.JSGlobalObject) ?*anyopaque;
|
||||
pub extern fn Zig__GlobalObject__resetModuleRegistryMap(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) bool;
|
||||
pub extern fn ArrayBufferSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
|
||||
pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
|
||||
pub extern fn ArrayBufferSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
|
||||
pub extern fn ArrayBufferSink__detachPtr(JSValue0: JSC__JSValue) void;
|
||||
pub extern fn ArrayBufferSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
|
||||
pub extern fn ArrayBufferSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
|
||||
pub extern fn ArrayBufferSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
|
||||
pub extern fn ArrayBufferSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
|
||||
pub extern fn HTTPSResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
|
||||
pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
|
||||
pub extern fn HTTPSResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
|
||||
pub extern fn HTTPSResponseSink__detachPtr(JSValue0: JSC__JSValue) void;
|
||||
pub extern fn HTTPSResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
|
||||
pub extern fn HTTPSResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
|
||||
pub extern fn HTTPSResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
|
||||
pub extern fn HTTPSResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
|
||||
pub extern fn HTTPResponseSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
|
||||
pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
|
||||
pub extern fn HTTPResponseSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
|
||||
pub extern fn HTTPResponseSink__detachPtr(JSValue0: JSC__JSValue) void;
|
||||
pub extern fn HTTPResponseSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
|
||||
pub extern fn HTTPResponseSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
|
||||
pub extern fn HTTPResponseSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
|
||||
pub extern fn HTTPResponseSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
|
||||
pub extern fn FileSink__assignToStream(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, arg2: ?*anyopaque, arg3: [*c]*anyopaque) JSC__JSValue;
|
||||
pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque, onDestroyPtrTag: usize) JSC__JSValue;
|
||||
pub extern fn FileSink__createObject(arg0: *bindings.JSGlobalObject, arg1: ?*anyopaque) JSC__JSValue;
|
||||
pub extern fn FileSink__detachPtr(JSValue0: JSC__JSValue) void;
|
||||
pub extern fn FileSink__setDestroyCallback(JSValue0: JSC__JSValue, callback: usize) void;
|
||||
pub extern fn FileSink__fromJS(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*anyopaque;
|
||||
pub extern fn FileSink__onClose(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) void;
|
||||
pub extern fn FileSink__onReady(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue) void;
|
||||
|
||||
@@ -1528,40 +1528,6 @@ const AutoFlusher = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const SinkDestructor = struct {
|
||||
const Detached = opaque {};
|
||||
const Subprocess = JSC.API.Bun.Subprocess;
|
||||
pub const Ptr = bun.TaggedPointerUnion(.{
|
||||
Detached,
|
||||
Subprocess,
|
||||
});
|
||||
|
||||
pub export fn Bun__onSinkDestroyed(
|
||||
ptr_value: ?*anyopaque,
|
||||
sink_ptr: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
_ = sink_ptr; // autofix
|
||||
const ptr = Ptr.from(ptr_value);
|
||||
|
||||
if (ptr.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ptr.tag()) {
|
||||
.Detached => {
|
||||
return;
|
||||
},
|
||||
.Subprocess => {
|
||||
const subprocess = ptr.as(Subprocess);
|
||||
subprocess.onStdinDestroyed();
|
||||
},
|
||||
else => {
|
||||
Output.debugWarn("Unknown sink type", .{});
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
|
||||
return struct {
|
||||
sink: SinkType,
|
||||
@@ -1610,10 +1576,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
|
||||
return shim.cppFn("onStart", .{ ptr, globalThis });
|
||||
}
|
||||
|
||||
pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque, destructor: usize) callconv(.C) JSValue {
|
||||
pub fn createObject(globalThis: *JSGlobalObject, object: *anyopaque) callconv(.C) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
return shim.cppFn("createObject", .{ globalThis, object, destructor });
|
||||
return shim.cppFn("createObject", .{ globalThis, object });
|
||||
}
|
||||
|
||||
pub fn fromJS(globalThis: *JSGlobalObject, value: JSValue) ?*anyopaque {
|
||||
@@ -1622,12 +1588,6 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
|
||||
return shim.cppFn("fromJS", .{ globalThis, value });
|
||||
}
|
||||
|
||||
pub fn setDestroyCallback(value: JSValue, callback: usize) void {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
return shim.cppFn("setDestroyCallback", .{ value, callback });
|
||||
}
|
||||
|
||||
pub fn construct(globalThis: *JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
@@ -1651,7 +1611,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type {
|
||||
return .undefined;
|
||||
};
|
||||
this.sink.construct(allocator);
|
||||
return createObject(globalThis, this, 0);
|
||||
return createObject(globalThis, this);
|
||||
}
|
||||
|
||||
pub fn finalize(ptr: *anyopaque) callconv(.C) void {
|
||||
@@ -2954,6 +2914,7 @@ pub const FileSink = struct {
|
||||
is_socket: bool = false,
|
||||
fd: bun.FileDescriptor = bun.invalid_fd,
|
||||
has_js_called_unref: bool = false,
|
||||
island: ?*JSC.Subprocess.Island = null,
|
||||
|
||||
const log = Output.scoped(.FileSink, false);
|
||||
|
||||
@@ -3367,11 +3328,11 @@ pub const FileSink = struct {
|
||||
}
|
||||
|
||||
pub fn toJS(this: *FileSink, globalThis: *JSGlobalObject) JSValue {
|
||||
return JSSink.createObject(globalThis, this, 0);
|
||||
return JSSink.createObject(globalThis, this);
|
||||
}
|
||||
|
||||
pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject, destructor: ?SinkDestructor.Ptr) JSValue {
|
||||
return JSSink.createObject(globalThis, this, if (destructor) |dest| @intFromPtr(dest.ptr()) else 0);
|
||||
pub fn toJSWithDestructor(this: *FileSink, globalThis: *JSGlobalObject) JSValue {
|
||||
return JSSink.createObject(globalThis, this);
|
||||
}
|
||||
|
||||
pub fn endFromJS(this: *FileSink, globalThis: *JSGlobalObject) JSC.Maybe(JSValue) {
|
||||
|
||||
@@ -64,7 +64,7 @@ function header() {
|
||||
class ${className} final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t destructor = 0);
|
||||
static ${className}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr);
|
||||
static constexpr SinkID Sink = SinkID::${name};
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
@@ -106,13 +106,11 @@ function header() {
|
||||
void* m_sinkPtr;
|
||||
int m_refCount { 1 };
|
||||
|
||||
uintptr_t m_onDestroy { 0 };
|
||||
|
||||
${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
|
||||
${className}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
m_sinkPtr = sinkPtr;
|
||||
m_onDestroy = onDestroy;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
@@ -123,7 +121,7 @@ function header() {
|
||||
class ${controller} final : public JSC::JSDestructibleObject {
|
||||
public:
|
||||
using Base = JSC::JSDestructibleObject;
|
||||
static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy);
|
||||
static ${controller}* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr);
|
||||
static constexpr SinkID Sink = SinkID::${name};
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
@@ -162,13 +160,10 @@ function header() {
|
||||
mutable WriteBarrier<JSC::Unknown> m_onClose;
|
||||
mutable JSC::Weak<JSObject> m_weakReadableStream;
|
||||
|
||||
uintptr_t m_onDestroy { 0 };
|
||||
|
||||
${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
|
||||
${controller}(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
m_sinkPtr = sinkPtr;
|
||||
m_onDestroy = onDestroy;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
@@ -566,10 +561,6 @@ const ClassInfo ${controller}::s_info = { "${controllerName}"_s, &Base::s_info,
|
||||
|
||||
${className}::~${className}()
|
||||
{
|
||||
if (m_onDestroy) {
|
||||
Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr);
|
||||
}
|
||||
|
||||
if (m_sinkPtr) {
|
||||
${name}__finalize(m_sinkPtr);
|
||||
}
|
||||
@@ -578,10 +569,6 @@ ${className}::~${className}()
|
||||
|
||||
${controller}::~${controller}()
|
||||
{
|
||||
if (m_onDestroy) {
|
||||
Bun__onSinkDestroyed(m_onDestroy, m_sinkPtr);
|
||||
}
|
||||
|
||||
if (m_sinkPtr) {
|
||||
${name}__finalize(m_sinkPtr);
|
||||
}
|
||||
@@ -598,12 +585,6 @@ JSObject* JS${controllerName}::createPrototype(VM& vm, JSDOMGlobalObject& global
|
||||
}
|
||||
|
||||
void JS${controllerName}::detach() {
|
||||
if (m_onDestroy) {
|
||||
auto destroy = m_onDestroy;
|
||||
m_onDestroy = 0;
|
||||
Bun__onSinkDestroyed(destroy, m_sinkPtr);
|
||||
}
|
||||
|
||||
m_sinkPtr = nullptr;
|
||||
m_onPull.clear();
|
||||
|
||||
@@ -635,16 +616,16 @@ ${constructor}* ${constructor}::create(JSC::VM& vm, JSC::JSGlobalObject* globalO
|
||||
return ptr;
|
||||
}
|
||||
|
||||
${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
|
||||
${className}* ${className}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr)
|
||||
{
|
||||
${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr, onDestroy);
|
||||
${className}* ptr = new (NotNull, JSC::allocateCell<${className}>(vm)) ${className}(vm, structure, sinkPtr);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr, uintptr_t onDestroy)
|
||||
${controller}* ${controller}::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* sinkPtr)
|
||||
{
|
||||
${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr, onDestroy);
|
||||
${controller}* ptr = new (NotNull, JSC::allocateCell<${controller}>(vm)) ${controller}(vm, structure, sinkPtr);
|
||||
ptr->finishCreation(vm);
|
||||
return ptr;
|
||||
}
|
||||
@@ -697,16 +678,6 @@ void ${controller}::finishCreation(VM& vm)
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
|
||||
extern "C" void ${name}__setDestroyCallback(EncodedJSValue encodedValue, uintptr_t callback)
|
||||
{
|
||||
JSValue value = JSValue::decode(encodedValue);
|
||||
if (auto* sink = JSC::jsDynamicCast<WebCore::${className}*>(value)) {
|
||||
sink->m_onDestroy = callback;
|
||||
} else if (auto* controller = JSC::jsDynamicCast<WebCore::${controller}*>(value)) {
|
||||
controller->m_onDestroy = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void ${className}::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
|
||||
{
|
||||
auto* thisObject = jsCast<${className}*>(cell);
|
||||
@@ -844,12 +815,12 @@ default:
|
||||
const { className, controller, prototypeName, controllerPrototypeName, constructor } = names(name);
|
||||
|
||||
templ += `
|
||||
extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr, uintptr_t destructor)
|
||||
extern "C" JSC__JSValue ${name}__createObject(JSC__JSGlobalObject* arg0, void* sinkPtr)
|
||||
{
|
||||
auto& vm = arg0->vm();
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
|
||||
JSC::Structure* structure = globalObject->${name}Structure();
|
||||
return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr, destructor));
|
||||
return JSC::JSValue::encode(WebCore::JS${name}::create(vm, globalObject, structure, sinkPtr));
|
||||
}
|
||||
|
||||
extern "C" void* ${name}__fromJS(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1)
|
||||
@@ -883,7 +854,7 @@ extern "C" JSC__JSValue ${name}__assignToStream(JSC__JSGlobalObject* arg0, JSC__
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0);
|
||||
|
||||
JSC::Structure* structure = WebCore::getDOMStructure<WebCore::${controller}>(vm, *globalObject);
|
||||
WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr, 0);
|
||||
WebCore::${controller} *controller = WebCore::${controller}::create(vm, globalObject, structure, sinkPtr);
|
||||
*controllerValue = reinterpret_cast<void*>(JSC::JSValue::encode(controller));
|
||||
return globalObject->assignToStream(JSC::JSValue::decode(stream), controller);
|
||||
}
|
||||
|
||||
@@ -246,36 +246,7 @@ pub const ShellSubprocess = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toJS(this: *Writable, globalThis: *JSC.JSGlobalObject, subprocess: *Subprocess) JSValue {
|
||||
return switch (this.*) {
|
||||
.fd => |fd| JSValue.jsNumber(fd),
|
||||
.memfd, .ignore => JSValue.jsUndefined(),
|
||||
.buffer, .inherit => JSValue.jsUndefined(),
|
||||
.pipe => |pipe| {
|
||||
this.* = .{ .ignore = {} };
|
||||
if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) {
|
||||
pipe.onAttachedProcessExit();
|
||||
return pipe.toJS(globalThis);
|
||||
} else {
|
||||
subprocess.flags.has_stdin_destructor_called = false;
|
||||
subprocess.weak_file_sink_stdin_ptr = pipe;
|
||||
return pipe.toJSWithDestructor(
|
||||
globalThis,
|
||||
JSC.WebCore.SinkDestructor.Ptr.init(subprocess),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn finalize(this: *Writable) void {
|
||||
const subprocess: *Subprocess = @fieldParentPtr("stdin", this);
|
||||
if (subprocess.this_jsvalue != .zero) {
|
||||
if (JSC.Codegen.JSSubprocess.stdinGetCached(subprocess.this_jsvalue)) |existing_value| {
|
||||
JSC.WebCore.FileSink.JSSink.setDestroyCallback(existing_value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return switch (this.*) {
|
||||
.pipe => |pipe| {
|
||||
pipe.deref();
|
||||
|
||||
33
test/js/bun/spawn/spawn-stdin-regression.test.js
Normal file
33
test/js/bun/spawn/spawn-stdin-regression.test.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { bunExe } from "harness";
|
||||
|
||||
const jsc = require("bun:jsc");
|
||||
|
||||
test(`this used to crash`, async () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const buffer = Buffer.alloc(1024 * 1024, "a");
|
||||
|
||||
async function getStdin() {
|
||||
{
|
||||
let subprocess = Bun.spawn({
|
||||
cmd: [bunExe(), "-e", "Bun.sleep(100)"],
|
||||
stdio: ["pipe", "ignore", "ignore"],
|
||||
});
|
||||
subprocess.unref();
|
||||
subprocess.stdin.write(buffer);
|
||||
process.kill(subprocess.pid, "SIGKILL");
|
||||
await subprocess.stdin.end().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
async function iter() {
|
||||
await getStdin();
|
||||
}
|
||||
|
||||
await Promise.all(Array.from({ length: 200 }, () => iter()));
|
||||
Bun.gc(true);
|
||||
await Bun.sleep(10);
|
||||
const { objectTypeCounts } = jsc.heapStats();
|
||||
console.log("objectTypeCounts:", objectTypeCounts.FileSink, objectTypeCounts.Subprocess);
|
||||
console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB");
|
||||
}
|
||||
}, 30_000);
|
||||
Reference in New Issue
Block a user