add mutex locks around modifications to child process array

This commit is contained in:
Emil Lerch 2025-10-23 14:15:28 -07:00
parent 506b877da2
commit 8a208c237b
Signed by: lobo
GPG key ID: A7B62D657EF764F8

View file

@ -19,6 +19,7 @@ const SpeechHandler = struct {
recoverable_error_count: u32 = 0,
exec_program: ?[]const u8 = null,
child_processes: std.ArrayList(*Process) = .{},
child_processes_mutex: std.Thread.Mutex = .{},
reclaiming: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
const max_children = 5;
@ -52,6 +53,10 @@ const SpeechHandler = struct {
}
fn exec(self: *SpeechHandler, text: []const u8) !void {
const program = self.exec_program.?; // should only be called when exec_program is not null
self.child_processes_mutex.lock();
defer self.child_processes_mutex.unlock();
// We need to be able to clean up at some point in the future, but we don't
// care about these processes otherwise
const process = try self.allocator.create(Process);
@ -76,6 +81,10 @@ const SpeechHandler = struct {
// This code should present that
if (self.reclaiming.cmpxchgStrong(false, true, .acquire, .acquire)) |_| return;
defer self.reclaiming.store(false, .release);
self.child_processes_mutex.lock();
defer self.child_processes_mutex.unlock();
if (!reap_all and self.child_processes.items.len <= max_children) return;
std.log.debug("Reclaiming memory from {s} processes", .{if (reap_all) "ALL" else "completed"});
if (self.child_processes.items.len == 0) return;
@ -268,6 +277,9 @@ fn signalAction(sig: i32, info: *const std.posix.siginfo_t, _: ?*anyopaque) call
if (sig == std.posix.SIG.CHLD) {
const pid = info.fields.common.first.piduid.pid;
std.log.debug("SIGCHLD on pid {d}", .{pid});
// IMPORTANT: Cannot use mutex in signal handler - risk of deadlock
// Just iterate without lock since we're only reading/nulling pointers
for (handler.child_processes.items) |proc| {
if (proc.child) |child| {
if (child.id == pid) {