diff --git a/packages/runtime/src/scr_worker.c b/packages/runtime/src/scr_worker.c index 18312cef5..d9927e8c6 100644 --- a/packages/runtime/src/scr_worker.c +++ b/packages/runtime/src/scr_worker.c @@ -11,6 +11,33 @@ #include #endif +#if defined(__linux__) +#include +#include +#include + +/* Every worker's mailbox holds descriptors. Once a process passes 64 of + * them, the kernel grows its descriptor table, and while other threads share + * the table it first waits a full RCU grace period (synchronize_rcu in + * expand_fdtable): measured 11-25 ms on a 16-vCPU host, on the thread that + * starts the next worker. Growing the table at load, while the process still + * has one thread, never waits; the table never shrinks. 256 entries leave + * room for the most workers the allocator gives arenas to. */ +__attribute__((constructor)) static void scr_worker_reserve_fd_table(void) { + const int top = 255; + struct rlimit limit; + if (getrlimit(RLIMIT_NOFILE, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur <= (rlim_t)top) + return; + for (int fd = 0; fd <= 2; fd++) { + int copy = fcntl(fd, F_DUPFD_CLOEXEC, top); + if (copy >= 0) { + (void)close(copy); + return; + } + } +} +#endif + struct ScrWorkerThread { atomic_size_t references; atomic_bool cancel; diff --git a/packages/runtime/src/scr_worker.test.c b/packages/runtime/src/scr_worker.test.c index f8ecee48a..12065b43d 100644 --- a/packages/runtime/src/scr_worker.test.c +++ b/packages/runtime/src/scr_worker.test.c @@ -54,7 +54,32 @@ static int entry(uint32_t root_id) { return (int)root_id; } +#if defined(__linux__) +#include +#include +#include + +/* Worker builds grow the descriptor table at load, while the process has one + * thread: growing it later, with threads sharing it, waits a full RCU grace + * period on the thread that opens the next worker's mailbox. */ +static void check_fd_table_reserved(void) { + struct rlimit limit; + if (getrlimit(RLIMIT_NOFILE, &limit) != 0 || (limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur <= 255)) return; + FILE *status = fopen("/proc/self/status", "r"); + assert(status); + char line[256]; + long size = -1; + while (fgets(line, sizeof line, status)) + if (strncmp(line, "FDSize:", 7) == 0) size = strtol(line + 7, NULL, 10); + fclose(status); + assert(size >= 256); +} +#endif + int main(void) { +#if defined(__linux__) + check_fd_table_reserved(); +#endif scr_init(); scr_lib_init(0, NULL); ScrMailbox *mailbox = scr_mailbox_new();