Follow-up to #309 / PR #316 (Codex round 6, deferred by scope decision).
loadable_library_copy copies a built library to <lib>.rustcall.<host>.<pid>.<generation>.<ext> and, before copying, sweeps the copies of this host whose pid no longer exists (_sweep_stale_generation_copies, _process_alive). The host tag is a digest prefix of gethostname(); liveness is kill(pid, 0) on Unix and OpenProcess + GetExitCodeProcess on Windows.
Residual scenario
Two containers that share the library's volume and the UTS hostname but run in isolated pid namespaces can hold identical pids and identical generation counters, so:
- they can pick the same copy path — the second
cp overwrites the first's copy (harmless on Unix once mapped; on Windows the copy fails against the mapped DLL and loadable_library_copy falls back to Cargo's output in place);
- container B's sweep can take container A's running pid for dead and unlink A's copy in the window between A's
cp and its dlopen (after dlopen an unlink is harmless on Unix; on Windows the delete is refused).
Everything a single host can know locally has been used; deciding liveness across pid namespaces needs coordination on the shared volume.
Options
Acceptance
Follow-up to #309 / PR #316 (Codex round 6, deferred by scope decision).
loadable_library_copycopies a built library to<lib>.rustcall.<host>.<pid>.<generation>.<ext>and, before copying, sweeps the copies of this host whose pid no longer exists (_sweep_stale_generation_copies,_process_alive). The host tag is a digest prefix ofgethostname(); liveness iskill(pid, 0)on Unix andOpenProcess+GetExitCodeProcesson Windows.Residual scenario
Two containers that share the library's volume and the UTS hostname but run in isolated pid namespaces can hold identical pids and identical generation counters, so:
cpoverwrites the first's copy (harmless on Unix once mapped; on Windows the copy fails against the mapped DLL andloadable_library_copyfalls back to Cargo's output in place);cpand itsdlopen(afterdlopenan unlink is harmless on Unix; on Windows the delete is refused).Everything a single host can know locally has been used; deciding liveness across pid namespaces needs coordination on the shared volume.
Options
<host>.<pid>.<instance>.<gen>) removes (1) but not (2): the sweep still cannot tell a foreign namespace's live pid from a dead one.<copy>.lease, holder writes a heartbeat / exclusive-create claim, sweep removes only copies whose lease is stale) solves both, and is the same shape as the lockfile claim of Cargo dependency builds are unpinned and online-only: no Cargo.lock, no --locked, cache keyed on version ranges #256 / PR Pin // cargo-deps: builds to a persisted Cargo.lock; --locked, --offline, resolution in the key (#256) #313.RUSTCALL_CACHE_DIR-style, not shared between containers) sidesteps the problem for deployments that can configure it.Acceptance
test/test_hot_reload_transaction.jl("each reload opens a fresh path") keep passing.