const ProcessAutoKiller = @This(); const log = bun.Output.scoped(.AutoKiller, true); const bun = @import("bun"); const std = @import("std"); processes: std.AutoArrayHashMapUnmanaged(*bun.spawn.Process, void) = .{}, enabled: bool = false, ever_enabled: bool = false, pub fn enable(this: *ProcessAutoKiller) void { this.enabled = true; this.ever_enabled = true; } pub fn disable(this: *ProcessAutoKiller) void { this.enabled = false; } pub const Result = struct { processes: u32 = 0, pub fn format(self: @This(), comptime _: []const u8, _: anytype, writer: anytype) !void { switch (self.processes) { 0 => {}, 1 => { try writer.writeAll("killed 1 dangling process"); }, else => { try std.fmt.format(writer, "killed {d} dangling processes", .{self.processes}); }, } } }; pub fn kill(this: *ProcessAutoKiller) Result { return .{ .processes = this.killProcesses(), }; } fn killProcesses(this: *ProcessAutoKiller) u32 { var count: u32 = 0; while (this.processes.pop()) |process| { if (!process.key.hasExited()) { log("process.kill {d}", .{process.key.pid}); count += @as(u32, @intFromBool(process.key.kill(@intFromEnum(bun.SignalCode.default)) == .result)); } } return count; } pub fn clear(this: *ProcessAutoKiller) void { if (this.processes.capacity() > 256) { this.processes.clearAndFree(bun.default_allocator); } this.processes.clearRetainingCapacity(); } pub fn onSubprocessSpawn(this: *ProcessAutoKiller, process: *bun.spawn.Process) void { if (this.enabled) this.processes.put(bun.default_allocator, process, {}) catch {}; } pub fn onSubprocessExit(this: *ProcessAutoKiller, process: *bun.spawn.Process) void { if (this.ever_enabled) _ = this.processes.swapRemove(process); } pub fn deinit(this: *ProcessAutoKiller) void { this.processes.deinit(bun.default_allocator); }