Skip to content

Commit 96dae0a

Browse files
fix: force spawn (not fork) for sandbox subprocess to prevent CI OOM from inherited torch/coverage state
1 parent 09c7aea commit 96dae0a

1 file changed

Lines changed: 18 additions & 3 deletions

File tree

src/codefix_env/utils/sandbox.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,24 @@ def run_code(code: str, test_code: str = "", timeout_s: float = 5.0) -> Executio
344344
if err := _validate_ast(full_code):
345345
return ExecutionResult(exception=err, passed=False)
346346

347-
# Run in child process for isolation
348-
q: multiprocessing.Queue = multiprocessing.Queue()
349-
proc = multiprocessing.Process(
347+
# Run in child process for isolation.
348+
#
349+
# Explicitly use the "spawn" start method rather than the platform
350+
# default. On Linux, multiprocessing.Process() defaults to fork(),
351+
# which copies the ENTIRE parent memory image into every child --
352+
# including PyTorch, if anything in the same test/training session
353+
# already imported it (e.g. RewardMLP tests running earlier in the
354+
# same pytest process). Combined with coverage.py tracing every
355+
# forked child, running ~200 sandboxed executions in one CI session
356+
# compounded into an OOM kill on GitHub's 7GB runner, confirmed by
357+
# the kill point moving later (not disappearing) after the first
358+
# resource-cleanup fix -- the leak was real but this was the larger
359+
# cause. spawn starts each child as a fresh, minimal interpreter that
360+
# does not inherit torch or the parent's coverage trace state, at the
361+
# cost of slightly higher per-call startup time versus fork.
362+
ctx = multiprocessing.get_context("spawn")
363+
q: multiprocessing.Queue = ctx.Queue()
364+
proc = ctx.Process(
350365
target=_run_in_process,
351366
args=(full_code, full_test, q),
352367
daemon=True,

0 commit comments

Comments
 (0)