aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-04 08:09:23 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-12-04 08:09:23 -0800
commit5bb8c42bdbdda11f816c180d69afb8e89bf407bd (patch)
tree300150cd6cddd6808d56b0e136b2000222fc2e15 /src
parent69114ac0a9edc79bf95768666dac88d60bffb3b3 (diff)
downloadbun-5bb8c42bdbdda11f816c180d69afb8e89bf407bd.tar.gz
bun-5bb8c42bdbdda11f816c180d69afb8e89bf407bd.tar.zst
bun-5bb8c42bdbdda11f816c180d69afb8e89bf407bd.zip
[Bun.spawn] Introduce `Subprocess.prototype.signalCode`
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/bun.classes.ts3
-rw-r--r--src/bun.js/api/bun/subprocess.zig154
2 files changed, 118 insertions, 39 deletions
diff --git a/src/bun.js/api/bun.classes.ts b/src/bun.js/api/bun.classes.ts
index 04dd94a38..aeac59f05 100644
--- a/src/bun.js/api/bun.classes.ts
+++ b/src/bun.js/api/bun.classes.ts
@@ -55,6 +55,9 @@ export default [
exitCode: {
getter: "getExitCode",
},
+ signalCode: {
+ getter: "getSignalCode",
+ },
exited: {
getter: "getExited",
diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig
index ba1aab7ff..fe09746eb 100644
--- a/src/bun.js/api/bun/subprocess.zig
+++ b/src/bun.js/api/bun/subprocess.zig
@@ -35,6 +35,7 @@ pub const Subprocess = struct {
on_exit_callback: JSC.Strong = .{},
exit_code: ?u8 = null,
+ signal_code: ?SignalCode = null,
waitpid_err: ?JSC.Node.Syscall.Error = null,
has_waitpid_task: bool = false,
@@ -58,9 +59,45 @@ pub const Subprocess = struct {
}) = .{},
has_pending_activity: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true),
is_sync: bool = false,
+ this_jsvalue: JSC.JSValue = .zero,
+
+ pub const SignalCode = enum(u8) {
+ SIGHUP = 1,
+ SIGINT = 2,
+ SIGQUIT = 3,
+ SIGILL = 4,
+ SIGTRAP = 5,
+ SIGABRT = 6,
+ SIGBUS = 7,
+ SIGFPE = 8,
+ SIGKILL = 9,
+ SIGUSR1 = 10,
+ SIGSEGV = 11,
+ SIGUSR2 = 12,
+ SIGPIPE = 13,
+ SIGALRM = 14,
+ SIGTERM = 15,
+ SIG16 = 16,
+ SIGCHLD = 17,
+ SIGCONT = 18,
+ SIGSTOP = 19,
+ SIGTSTP = 20,
+ SIGTTIN = 21,
+ SIGTTOU = 22,
+ SIGURG = 23,
+ SIGXCPU = 24,
+ SIGXFSZ = 25,
+ SIGVTALRM = 26,
+ SIGPROF = 27,
+ SIGWINCH = 28,
+ SIGIO = 29,
+ SIGPWR = 30,
+ SIGSYS = 31,
+ _,
+ };
pub fn hasExited(this: *const Subprocess) bool {
- return this.exit_code != null or this.waitpid_err != null;
+ return this.exit_code != null or this.waitpid_err != null or this.signal_code != null;
}
pub fn updateHasPendingActivityFlag(this: *Subprocess) void {
@@ -308,6 +345,8 @@ pub const Subprocess = struct {
globalThis: *JSGlobalObject,
callframe: *JSC.CallFrame,
) callconv(.C) JSValue {
+ this.this_jsvalue = callframe.this();
+
var arguments = callframe.arguments(1);
// If signal is 0, then no actual signal is sent, but error checking
// is still performed.
@@ -945,6 +984,21 @@ pub const Subprocess = struct {
return JSC.JSValue.jsNull();
}
+ pub fn getSignalCode(
+ this: *Subprocess,
+ global: *JSGlobalObject,
+ ) callconv(.C) JSValue {
+ if (this.signal_code) |signal| {
+ const value = @enumToInt(signal);
+ if (value < @enumToInt(SignalCode.SIGSYS) + 1)
+ return JSC.ZigString.init(std.mem.span(@tagName(signal))).toValueGC(global)
+ else
+ return JSC.JSValue.jsNumber(value);
+ }
+
+ return JSC.JSValue.jsNull();
+ }
+
pub fn spawn(globalThis: *JSC.JSGlobalObject, args: JSValue, secondaryArgsValue: ?JSValue) JSValue {
return spawnMaybeSync(globalThis, args, secondaryArgsValue, false);
}
@@ -1309,6 +1363,7 @@ pub const Subprocess = struct {
subprocess.toJS(globalThis)
else
JSValue.zero;
+ subprocess.this_jsvalue = out;
if (comptime !is_sync) {
var poll = JSC.FilePoll.init(jsc_vm, pidfd, .{}, Subprocess, subprocess);
@@ -1421,6 +1476,26 @@ pub const Subprocess = struct {
}
pub fn wait(this: *Subprocess, sync: bool) void {
+ return this.waitWithJSValue(sync, this.this_jsvalue);
+ }
+
+ pub fn watch(this: *Subprocess) void {
+ if (this.poll_ref) |poll| {
+ _ = poll.register(
+ this.globalThis.bunVM().uws_event_loop.?,
+ .process,
+ true,
+ );
+ } else {
+ @panic("Internal Bun error: poll_ref in Subprocess is null unexpectedly. Please file a bug report.");
+ }
+ }
+
+ pub fn waitWithJSValue(
+ this: *Subprocess,
+ sync: bool,
+ this_jsvalue: JSC.JSValue,
+ ) void {
if (this.has_waitpid_task) {
return;
}
@@ -1433,24 +1508,24 @@ pub const Subprocess = struct {
this.waitpid_err = err;
},
.result => |result| {
- this.exit_code = @truncate(
- u8,
- brk: {
- if (std.os.W.IFEXITED(result.status)) {
- break :brk std.os.W.EXITSTATUS(result.status);
- } else if (std.os.W.IFSIGNALED(result.status)) {
- break :brk std.os.W.TERMSIG(result.status);
- } else if (std.os.W.IFSTOPPED(result.status)) {
- break :brk std.os.W.STOPSIG(result.status);
- } else {
- break :brk 1;
- }
- },
- );
+ if (std.os.W.IFEXITED(result.status)) {
+ this.exit_code = @truncate(u8, std.os.W.EXITSTATUS(result.status));
+ }
+
+ if (std.os.W.IFSIGNALED(result.status)) {
+ this.signal_code = @intToEnum(SignalCode, @truncate(u8, std.os.W.TERMSIG(result.status)));
+ } else if (std.os.W.IFSTOPPED(result.status)) {
+ this.signal_code = @intToEnum(SignalCode, @truncate(u8, std.os.W.STOPSIG(result.status)));
+ }
+
+ if (!this.hasExited()) {
+ this.watch();
+ }
},
}
+ this.has_waitpid_task = false;
- if (!sync) {
+ if (!sync and this.hasExited()) {
var vm = this.globalThis.bunVM();
// prevent duplicate notifications
@@ -1459,37 +1534,37 @@ pub const Subprocess = struct {
poll.deinitWithVM(vm);
}
- this.onExit(this.globalThis);
+ this.onExit(this.globalThis, this_jsvalue);
}
}
- fn onExit(this: *Subprocess, globalThis: *JSC.JSGlobalObject) void {
- // this.stdin.close();
- // this.stdout.close();
- // this.stderr.close();
-
+ fn onExit(
+ this: *Subprocess,
+ globalThis: *JSC.JSGlobalObject,
+ this_jsvalue: JSC.JSValue,
+ ) void {
defer this.updateHasPendingActivity();
+ this_jsvalue.ensureStillAlive();
this.has_waitpid_task = false;
- if (this.exit_promise.trySwap()) |promise| {
- if (this.exit_code) |code| {
- promise.asPromise().?.resolve(globalThis, JSValue.jsNumber(code));
- } else if (this.waitpid_err) |err| {
- this.waitpid_err = null;
- promise.asPromise().?.reject(globalThis, err.toJSC(globalThis));
- } else {
- // crash in debug mode
- if (comptime Environment.allow_assert)
- unreachable;
+ if (this.hasExited()) {
+ if (this.exit_promise.trySwap()) |promise| {
+ if (this.exit_code) |code| {
+ promise.asPromise().?.resolve(globalThis, JSValue.jsNumber(code));
+ } else if (this.signal_code != null) {
+ promise.asPromise().?.resolve(globalThis, this.getSignalCode(globalThis));
+ } else if (this.waitpid_err) |err| {
+ this.waitpid_err = null;
+ promise.asPromise().?.reject(globalThis, err.toJSC(globalThis));
+ } else {
+ // crash in debug mode
+ if (comptime Environment.allow_assert)
+ unreachable;
+ }
}
}
if (this.on_exit_callback.trySwap()) |callback| {
- const exit_value: JSValue = if (this.exit_code) |code|
- JSC.JSValue.jsNumber(code)
- else
- JSC.JSValue.jsNumber(@as(i32, -1));
-
const waitpid_value: JSValue =
if (this.waitpid_err) |err|
err.toJSC(globalThis)
@@ -1511,7 +1586,8 @@ pub const Subprocess = struct {
}
}
- this.unref();
+ if (this.hasExited())
+ this.unref();
}
const os = std.os;
@@ -1638,7 +1714,7 @@ pub const Subprocess = struct {
} else if (str.eqlComptime("pipe")) {
stdio_array[i] = Stdio{ .pipe = null };
} else {
- globalThis.throwInvalidArguments("stdio must be an array of 'inherit', 'ignore', or null", .{});
+ globalThis.throwInvalidArguments("stdio must be an array of 'inherit', 'pipe', 'ignore', Bun.file(pathOrFd), number, or null", .{});
return false;
}