Compare commits

...

10 Commits

Author SHA1 Message Date
Meghan Denny
4955eb0b2a lower this because windows leaks bad and exhibits the crash quickly 2024-07-31 23:51:11 -07:00
Meghan Denny
b9fc52bf76 move to .test.js file and make one test 2024-07-31 23:39:56 -07:00
Meghan Denny
e331fea16a fix again and add reproduction test 2024-07-31 22:31:32 -07:00
Meghan Denny
a116bf9e7b vscode: dont auto include this 2024-07-31 22:29:19 -07:00
Meghan Denny
714324e891 use bun.New 2024-07-31 18:50:37 -07:00
Meghan Denny
000ea07cbe fix posix crash 2024-07-31 18:15:19 -07:00
Meghan Denny
5e5ecd14f2 fix posix build error 2024-07-31 18:15:12 -07:00
Meghan Denny
8219f155b6 remove flags.finalized 2024-07-31 18:15:03 -07:00
Meghan Denny
d6757df1a6 remove flags.has_stdin_destructor_called 2024-07-31 18:14:45 -07:00
Meghan Denny
66d71fde0e Subprocess: fix crash when it frees itself before stdin does 2024-07-31 16:23:57 -07:00
8 changed files with 108 additions and 195 deletions

1
.vscode/launch.json generated vendored
View File

@@ -151,7 +151,6 @@
"env": {
"FORCE_COLOR": "0",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_DEBUG_EventLoop": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole",

View File

@@ -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();
}
};
};

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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();

View 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);