Compare commits

...

9 Commits

Author SHA1 Message Date
Jarred Sumner
58d45da47e Lost in the merge 2024-03-15 02:55:11 +01:00
Jarred Sumner
8f5c4aaefd Update Dockerfile 2024-03-15 01:58:28 +01:00
Jarred Sumner
45c00b1d9a seems to work? 2024-03-15 01:38:50 +01:00
Jarred Sumner
63d1512d62 ignore 2024-03-14 19:53:09 +01:00
Jarred Sumner
c9d5bd930e Fix test 2024-03-14 08:12:33 +01:00
Jarred Sumner
9e67e2a524 fixup 2024-03-14 08:10:13 +01:00
Jarred Sumner
ab274b92eb Test it on macOS and windows too why not 2024-03-14 08:06:28 +01:00
Jarred Sumner
71368b60fa Fixup 2024-03-14 08:01:53 +01:00
Jarred Sumner
85c4e87ccc Fixes #9404 2024-03-14 08:00:24 +01:00
7 changed files with 102 additions and 30 deletions

View File

@@ -16,7 +16,7 @@ ARG BUILD_MACHINE_ARCH=x86_64
ARG BUILDARCH=amd64
ARG TRIPLET=${ARCH}-linux-gnu
ARG GIT_SHA=""
ARG BUN_VERSION="bun-v1.0.7"
ARG BUN_VERSION="bun-v1.0.30"
ARG BUN_DOWNLOAD_URL_BASE="https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/${BUN_VERSION}"
ARG CANARY=0
ARG ASSERTIONS=OFF

View File

@@ -4195,19 +4195,19 @@ declare module "bun" {
};
/**
* The amount of CPU time used by the process, in nanoseconds.
* The amount of CPU time used by the process, in microseconds.
*/
cpuTime: {
/**
* User CPU time used by the process, in nanoseconds.
* User CPU time used by the process, in microseconds.
*/
user: number;
/**
* System CPU time used by the process, in nanoseconds.
* System CPU time used by the process, in microseconds.
*/
system: number;
/**
* Total CPU time used by the process, in nanoseconds.
* Total CPU time used by the process, in microseconds.
*/
total: number;
};

View File

@@ -205,7 +205,7 @@ pub const Process = struct {
} else if (comptime Environment.isWindows) {}
}
pub fn onWaitPidFromWaiterThread(this: *Process, waitpid_result: *const JSC.Maybe(PosixSpawn.WaitPidResult)) void {
pub fn onWaitPidFromWaiterThread(this: *Process, waitpid_result: *const JSC.Maybe(PosixSpawn.WaitPidResult), rusage: *const Rusage) void {
if (comptime Environment.isWindows) {
@compileError("not implemented on this platform");
}
@@ -213,7 +213,7 @@ pub const Process = struct {
this.poller.waiter_thread.unref(this.event_loop);
this.poller = .{ .detached = {} };
}
this.onWaitPid(waitpid_result, &std.mem.zeroes(Rusage));
this.onWaitPid(waitpid_result, rusage);
this.deref();
}
@@ -642,6 +642,8 @@ pub const WaiterThread = if (Environment.isPosix) WaiterThreadPosix else struct
}
pub fn setShouldUseWaiterThread() void {}
pub fn reloadHandlers() void {}
};
// Machines which do not support pidfd_open (GVisor, Linux Kernel < 5.6)
@@ -649,7 +651,6 @@ pub const WaiterThread = if (Environment.isPosix) WaiterThreadPosix else struct
// We use a single thread to call waitpid() in a loop.
const WaiterThreadPosix = struct {
started: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
signalfd: if (Environment.isLinux) bun.FileDescriptor else u0 = undefined,
eventfd: if (Environment.isLinux) bun.FileDescriptor else u0 = undefined,
js_process: ProcessQueue = .{},
@@ -672,6 +673,7 @@ const WaiterThreadPosix = struct {
pub const ResultTask = struct {
result: JSC.Maybe(PosixSpawn.WaitPidResult),
subprocess: *T,
rusage: Rusage,
pub usingnamespace bun.New(@This());
@@ -680,8 +682,9 @@ const WaiterThreadPosix = struct {
pub fn runFromMainThread(self: *@This()) void {
const result = self.result;
const subprocess = self.subprocess;
const rusage = self.rusage;
self.destroy();
subprocess.onWaitPidFromWaiterThread(&result);
subprocess.onWaitPidFromWaiterThread(&result, &rusage);
}
pub fn runFromMainThreadMini(self: *@This(), _: *void) void {
@@ -702,7 +705,7 @@ const WaiterThreadPosix = struct {
const result = self.result;
const subprocess = self.subprocess;
self.destroy();
subprocess.onWaitPidFromWaiterThread(&result);
subprocess.onWaitPidFromWaiterThread(&result, &std.mem.zeroes(Rusage));
}
pub fn runFromMainThreadMini(self: *@This(), _: *void) void {
@@ -741,7 +744,8 @@ const WaiterThreadPosix = struct {
continue;
}
const result = PosixSpawn.wait4(pid, std.os.W.NOHANG, null);
var rusage = std.mem.zeroes(Rusage);
const result = PosixSpawn.wait4(pid, std.os.W.NOHANG, &rusage);
if (result == .err or (result == .result and result.result.pid == pid)) {
_ = this.active.orderedRemove(i);
queue = this.active.items;
@@ -754,6 +758,7 @@ const WaiterThreadPosix = struct {
.{
.result = result,
.subprocess = process,
.rusage = rusage,
},
),
)),
@@ -813,33 +818,47 @@ const WaiterThreadPosix = struct {
return;
}
var thread = try std.Thread.spawn(.{ .stack_size = stack_size }, loop, .{});
thread.detach();
if (comptime Environment.isLinux) {
const linux = std.os.linux;
var mask = std.os.empty_sigset;
linux.sigaddset(&mask, std.os.SIG.CHLD);
instance.signalfd = bun.toFD(try std.os.signalfd(-1, &mask, linux.SFD.CLOEXEC | linux.SFD.NONBLOCK));
instance.eventfd = bun.toFD(try std.os.eventfd(0, linux.EFD.NONBLOCK | linux.EFD.CLOEXEC | 0));
}
var thread = try std.Thread.spawn(.{ .stack_size = stack_size }, loop, .{});
thread.detach();
}
fn wakeup(_: c_int) callconv(.C) void {
const one = @as([8]u8, @bitCast(@as(usize, 1)));
_ = bun.sys.write(instance.eventfd, &one).unwrap() catch 0;
}
pub fn reloadHandlers() void {
if (!should_use_waiter_thread) {
return;
}
if (comptime Environment.isLinux) {
var current_mask = std.os.empty_sigset;
std.os.linux.sigaddset(&current_mask, std.os.SIG.CHLD);
const act = std.os.Sigaction{
.handler = .{ .handler = &wakeup },
.mask = current_mask,
.flags = std.os.SA.NOCLDSTOP,
};
std.os.sigaction(std.os.SIG.CHLD, &act, null) catch {};
}
}
pub fn loop() void {
Output.Source.configureNamedThread("Waitpid");
reloadHandlers();
var this = &instance;
while (true) {
outer: while (true) {
this.js_process.loop();
if (comptime Environment.isLinux) {
var polls = [_]std.os.pollfd{
.{
.fd = this.signalfd.cast(),
.events = std.os.POLL.IN | std.os.POLL.ERR,
.revents = 0,
},
.{
.fd = this.eventfd.cast(),
.events = std.os.POLL.IN | std.os.POLL.ERR,
@@ -847,11 +866,13 @@ const WaiterThreadPosix = struct {
},
};
_ = std.os.poll(&polls, std.math.maxInt(i32)) catch 0;
// Consume the pending eventfd
var buf: [8]u8 = undefined;
if (bun.sys.read(this.eventfd, &buf).unwrap() catch 0 > 0) {
continue :outer;
}
// Make sure we consume any pending signals
var buf: [1024]u8 = undefined;
_ = std.os.read(this.signalfd.cast(), &buf) catch 0;
_ = std.os.poll(&polls, std.math.maxInt(i32)) catch 0;
} else {
var mask = std.os.empty_sigset;
var signal: c_int = std.os.SIG.CHLD;

View File

@@ -1,5 +1,6 @@
// when we don't want to use @cInclude, we can just stick wrapper functions here
#include "root.h"
#include <csignal>
#include <cstdint>
#if !OS(WINDOWS)
@@ -295,7 +296,7 @@ static inline void make_pos_h_l(unsigned long* pos_h, unsigned long* pos_l,
extern "C" ssize_t sys_preadv2(int fd, const struct iovec* iov, int iovcnt,
off_t offset, unsigned int flags)
{
return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset>>32, RWF_NOWAIT);
return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset >> 32, RWF_NOWAIT);
}
extern "C" ssize_t sys_pwritev2(int fd, const struct iovec* iov, int iovcnt,
off_t offset, unsigned int flags)

View File

@@ -46,7 +46,7 @@ pub fn reloadHandlers() !void {
};
try setup_sigactions(&act);
@import("root").bun.spawn.WaiterThread.reloadHandlers();
bun_ignore_sigpipe();
}
const os = std.os;
@@ -59,6 +59,7 @@ pub fn start() !void {
try setup_sigactions(&act);
bun_ignore_sigpipe();
@import("root").bun.spawn.WaiterThread.reloadHandlers();
}
extern fn bun_ignore_sigpipe() void;

View File

@@ -0,0 +1,9 @@
const { spawn } = require("child_process");
if (!process.env.WITHOUT_WAITER_THREAD) {
if (!process.env.BUN_GARBAGE_COLLECTOR_LEVEL || !process.env.BUN_FEATURE_FLAG_FORCE_WAITER_THREAD) {
throw new Error("This test must be run with BUN_GARBAGE_COLLECTOR_LEVEL and BUN_FEATURE_FLAG_FORCE_WAITER_THREAD");
}
}
spawn("sleep", ["infinity"]);

View File

@@ -0,0 +1,40 @@
import { test, expect } from "bun:test";
import { spawn } from "bun";
import { bunEnv, bunExe } from "harness";
import { join } from "path";
async function run(withWaiterThread: boolean) {
const proc = spawn({
env: {
...bunEnv,
...(withWaiterThread
? { BUN_GARBAGE_COLLECTOR_LEVEL: "1", BUN_FEATURE_FLAG_FORCE_WAITER_THREAD: "1" }
: { WITHOUT_WAITER_THREAD: "1" }),
},
stderr: "inherit",
stdout: "inherit",
stdin: "ignore",
cmd: [bunExe(), join(__dirname, "spawn_waiter_thread-fixture.js")],
});
setTimeout(() => {
proc.kill(process.platform !== "win32" ? "SIGKILL" : undefined);
}, 1000).unref();
await proc.exited;
const resourceUsage = proc.resourceUsage();
// Assert we didn't use 100% of CPU time
console.log(resourceUsage.cpuTime);
expect(resourceUsage?.cpuTime.total).toBeLessThan(750_000n);
}
test("issue #9404", async () => {
const promises = [run(false)];
if (process.platform === "linux") {
promises.push(run(true));
}
await Promise.all(promises);
});