Compare commits

...

10 Commits

Author SHA1 Message Date
Meghan Denny
068602ecb8 make the unreachable actually unreachable 2024-01-26 14:02:42 -08:00
Meghan Denny
39dfbcb42e fix subprocess lifecycle in a followup 2024-01-25 23:34:19 -08:00
Meghan Denny
15624abffc delay windows timer callback to account for inaccuracy 2024-01-25 22:08:41 -08:00
Meghan Denny
dbfffab8a1 let CI tell me when this file is working 2024-01-25 19:09:27 -08:00
Meghan Denny
296ae1cf60 fix killed on linux and windows 2024-01-25 19:09:11 -08:00
Meghan Denny
e5ab3f94aa add more tests 2024-01-25 18:44:34 -08:00
Meghan Denny
2a2319e6ed add signalCode and exitCode 2024-01-25 18:44:09 -08:00
Meghan Denny
2266fe136f fix killed field 2024-01-25 18:43:56 -08:00
Meghan Denny
f89917078b set signal_code after kill() succeeds 2024-01-25 18:42:56 -08:00
Meghan Denny
0880133c10 default kill signal should be TERM 2024-01-25 18:42:42 -08:00
5 changed files with 49 additions and 14 deletions

View File

@@ -4038,7 +4038,8 @@ pub const Timer = struct {
_ = this.scheduled_count.fetchAdd(1, .Monotonic);
const ms: usize = @max(interval orelse this.interval, 1);
if (Environment.isWindows) {
if (uv.uv_timer_start(&this.timer, TimerReference.onUVRequest, @intCast(ms), 0) != 0) @panic("unable to start timer");
// add 10 to ms here since libuv/windows sometimes triggers callback slightly too early.
if (uv.uv_timer_start(&this.timer, TimerReference.onUVRequest, @intCast(ms + 10), 0) != 0) @panic("unable to start timer");
return;
}

View File

@@ -673,7 +673,7 @@ pub const Subprocess = struct {
var arguments = callframe.arguments(1);
// If signal is 0, then no actual signal is sent, but error checking
// is still performed.
var sig: i32 = 1;
var sig: i32 = 15; // TERM
if (arguments.len > 0) {
sig = arguments.ptr[0].coerce(i32, globalThis);
@@ -704,7 +704,7 @@ pub const Subprocess = struct {
return .{ .result = {} };
}
send_signal: {
{
if (comptime Environment.isLinux) {
// if these are the same, it means the pidfd is invalid.
if (!WaiterThread.shouldUseWaiterThread()) {
@@ -723,9 +723,9 @@ pub const Subprocess = struct {
// if the process was already killed don't throw
if (errno != .SRCH and errno != .NOSYS)
return .{ .err = bun.sys.Error.fromCode(errno, .kill) };
} else {
break :send_signal;
}
this.signal_code = SignalCode.from(sig);
return .{ .result = {} };
}
}
if (comptime Environment.isWindows) {
@@ -739,7 +739,7 @@ pub const Subprocess = struct {
if (err != .PERM)
return .{ .err = bun.sys.Error.fromCode(err, .kill) };
}
this.signal_code = SignalCode.from(sig);
return .{ .result = {} };
}
@@ -751,9 +751,9 @@ pub const Subprocess = struct {
if (errno != .SRCH)
return .{ .err = bun.sys.Error.fromCode(errno, .kill) };
}
this.signal_code = SignalCode.from(sig);
return .{ .result = {} };
}
return .{ .result = {} };
}
fn hasCalledGetter(this: *Subprocess, comptime getter: @Type(.EnumLiteral)) bool {

View File

@@ -981,6 +981,7 @@ class ChildProcess extends EventEmitter {
#exited = false;
#closesNeeded = 1;
#closesGot = 0;
#killed = false;
connected = false;
signalCode = null;
@@ -991,7 +992,9 @@ class ChildProcess extends EventEmitter {
channel;
get killed() {
if (this.#killed) return true;
if (this.#handle == null) return false;
return this.#handle.killed;
}
// constructor(options) {
@@ -1292,6 +1295,9 @@ class ChildProcess extends EventEmitter {
if (this.#handle) {
this.#handle.kill(signal);
}
this.#killed = true;
this.signalCode = this.#handle.signalCode;
this.exitCode = this.#handle.exitCode;
this.#maybeClose();

View File

@@ -210,9 +210,35 @@ describe("spawn()", () => {
expect(result1.trim()).toBe(Bun.which("sh"));
expect(result2.trim()).toBe(Bun.which("bash"));
});
it("should spawn a process synchronously", () => {
const { stdout } = spawnSync("echo", ["hello"], { encoding: "utf8" });
expect(stdout.trim()).toBe("hello");
it("should be able to be killed", () => {
const child = spawn(bunExe(), ["repl"]);
child.on("exit", () => {
// expect(child.killed).toBe(true);
expect(child.signalCode).toBe("SIGTERM");
expect(child.exitCode).toBe(null);
});
child.kill();
});
it("should allow a number to kill()", () => {
const child = spawn(bunExe(), ["repl"]);
child.on("exit", () => {
// expect(child.killed).toBe(true);
expect(child.signalCode).toBe("SIGQUIT");
expect(child.exitCode).toBe(null);
});
child.kill(3);
});
it("should allow a string to kill()", () => {
const child = spawn(bunExe(), ["repl"]);
child.on("exit", () => {
// expect(child.killed).toBe(true);
expect(child.signalCode).toBe("SIGQUIT");
expect(child.exitCode).toBe(null);
});
child.kill("SIGQUIT");
});
});

View File

@@ -1,9 +1,9 @@
// @known-failing-on-windows: 1 failing
import { describe, it, expect } from "bun:test";
import { bunExe, bunEnv, gc } from "harness";
import { readFileSync } from "fs";
import { join } from "path";
const isWindows = process.platform === "win32";
const TEST_WEBSOCKET_HOST = process.env.TEST_WEBSOCKET_HOST || "wss://ws.postman-echo.com/raw";
describe("WebSocket", () => {
@@ -507,7 +507,9 @@ describe("websocket in subprocess", () => {
subprocess.kill();
expect(await subprocess.exited).toBe(129);
expect(subprocess.killed).toBe(true);
expect(subprocess.signalCode).toBe("SIGTERM");
expect(subprocess.exitCode).toBe(null);
});
it("should exit with invalid url", async () => {
@@ -560,7 +562,7 @@ describe("websocket in subprocess", () => {
server.stop(true);
});
it("should exit after server stop and 0 messages", async () => {
it.skipIf(isWindows)("should exit after server stop and 0 messages", async () => {
const server = Bun.serve({
port: 0,
fetch(req, server) {