Run prefilter/postfilter shells via single-threaded worker processes

Forking and waitpid()-ing for the -C/-c shell filters used to happen
directly in the multi-threaded tiling threads, which made fork() expensive
(full COW of the parent's address space) and unsafe (other threads could
hold libc/malloc locks at fork time). Now we pre-fork a small pool of
single-threaded worker processes once, before any tiling or reader threads
are spawned, and the tiling threads ask a worker to fork+exec the shell
child on their behalf, receiving the input/output pipe fds back via
SCM_RIGHTS. The fork() and waitpid() happen entirely inside the
single-threaded worker, where they are cheap and only block that worker.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Erica Fischer
2026-05-30 14:50:37 -07:00
co-authored by Cursor
parent cb6cacef15
commit 4f4214ee23
4 changed files with 443 additions and 106 deletions
+9
View File
@@ -67,6 +67,7 @@
#include "attribute.hpp"
#include "thread.hpp"
#include "platform.hpp"
#include "plugin.hpp"
static int low_detail = 12;
static int full_detail = -1;
@@ -3714,6 +3715,14 @@ int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
// Pre-fork the filter worker pool while we are still single-threaded.
// Spawning them now (before any reader or tiling threads) means each
// worker is a clean, single-threaded process, so its later fork()s
// for the prefilter/postfilter shells are cheap and safe.
if (prefilter != NULL || postfilter != NULL) {
filter_workers_init(CPUS);
}
files_open_at_start = open(get_null_device(), O_RDONLY | O_CLOEXEC);
if (files_open_at_start < 0) {
perror("open /dev/null");