cli & replayer: Prevent Linux worker process orphaning with race-free PR_SET_PDEATHSIG#305
Open
louzt wants to merge 6 commits into
Open
cli & replayer: Prevent Linux worker process orphaning with race-free PR_SET_PDEATHSIG#305louzt wants to merge 6 commits into
louzt wants to merge 6 commits into
Conversation
When Fossilize is forcefully terminated or crashes, the child replay processes currently become orphans and continue to compile shaders in the background, leading to excessive CPU/RAM usage. This adds PR_SET_PDEATHSIG to ensure child processes are terminated immediately when the parent process dies, matching the expected lifecycle of the application.
…PDEATHSIG and CTest coverage
…tion-test for CI robustness
This was referenced Jul 24, 2026
…ns for multi-GPU filtering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background & Issue Description
When
fossilize_replayorExternalReplayerparent processes terminate unexpectedly (e.g.SIGKILL, Proton game exit, or crash), spawned child replayer workers can become orphaned or<defunct>. On Linux systems, these orphaned processes continue executing Vulkan SPIR-V pipeline compilations in the background, consuming ~50–100% CPU and GPU compute cycles indefinitely without being reclaimed.Cross-Platform Architectural Parity (Windows Job Objects vs. Linux
PR_SET_PDEATHSIG)On Windows, Fossilize already enforces automatic child process teardown via Windows Job Objects (
CreateJobObjectWconfigured withJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEinfossilize_external_replayer_windows.hpp). When a parent process dies on Windows, the OS kernel automatically terminates all associated child processes in the Job Object.On Linux, however, a feature disparity existed: child workers lacked kernel-level death signal bindings (
PR_SET_PDEATHSIG), allowing worker processes to detach and run indefinitely attached to PID 1. This PR brings Linux to full architectural parity with Windows Job Objects, while also introducing hardware-based multi-GPU selection parameters (--device-pci-vendorand--device-pci-device).Linked Issues
fossilize_replayfail to be killed /<defunct>zombie process accumulation on game exitTechnical & Architectural Overview
1. Cross-Platform Process Teardown Parity (Linux
PR_SET_PDEATHSIGvs. Windows JobObjects)CreateJobObjectW+AssignProcessToJobObjectwithJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.prctl(PR_SET_PDEATHSIG, SIGKILL)immediately uponfork(), coupled with a race-freegetppid() != parent_pidcheck. Once terminated, child processes are adopted and reaped by PID 1 (or container subreaperpv-adverbinpressure-vessel), preventing background execution and<defunct>zombie table accumulation.2. Multi-GPU Hardware Selection & Windows Hex Formatting (
--device-pci-vendor&--device-pci-device)To eliminate redundant pre-compilation on hybrid laptops (e.g. AMD Vega iGPU + NVIDIA RTX dGPU):
--device-pci-vendor <vendorID_hex>and--device-pci-device <deviceID_hex>parameters tocli/device.cpp,cli/fossilize_replay.cpp, andExternalReplayer::Options.0x%xviasnprintf) across both Linux and Windows platforms (fossilize_external_replayer_windows.hpp), avoiding decimal string conversion mismatches when parsed bystrtoul(..., 0).--device-pci-vendor 0x10defor NVIDIA), saving ~50% CPU/RAM overhead on dual-GPU laptops.3. Container PID Namespace & Kernel Portability (
pressure-vessel/bwrap)In Steam Linux Runtime and SteamOS (
pressure-vessel/srt-bwrap),fossilize_replaymaster and worker processes execute within the same container PID namespace.fork(),getppid()in the child returns the parent's container PID (bwrap, e.g.,pv-adverb), causinggetppid()to evaluate to1(or the subreaper PID).getppid() != parent_pidcheck remains fully coherent across container PID namespaces.4. DRM Subsystem, UMA Memory Reclamation & Signal Safety
SIGKILLtriggersclose()on the/dev/dri/renderD128or/dev/nvidia0file descriptors. The Linux DRM/GEM subsystem (drm_release()) atomically reclaims VRAM buffer objects and unmaps GPU MMU pages without VRAM leaks or driver instability.SIGKILLdelivered viaPR_SET_PDEATHSIGoperates orthogonally to internal signal handlers (SIGSEGV,SIGFPE,SIGILL,SIGBUS,SIGABRT).Process Flow Diagrams
1. Cross-Platform Process Teardown (Linux vs. Windows Parity)
flowchart TD subgraph Windows ["Windows OS Platform"] WinParent["Parent Process"] -->|Creates JobObject| WinJob["Job Object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)"] WinJob -->|Assigns| WinChild["Worker Subprocess"] WinParent -.->|On Parent Exit| WinKernel["Windows Kernel Auto-Kills Job Object Children"] end subgraph Linux ["Linux OS Platform (PR #305)"] LinParent["Parent Process (parent_pid)"] -->|forks| LinChild["Worker Subprocess"] LinChild -->|prctl| LinPdeath["PR_SET_PDEATHSIG(SIGKILL) + getppid() Check"] LinParent -.->|On Parent Exit| LinKernel["Linux Kernel Auto-Delivers SIGKILL to Workers"] end classDef platformStyle fill:#e1f5fe,stroke:#0288d1,stroke-width:2px; class Windows,Linux platformStyle;2. GPU Selection & Multi-Platform IPC Argument Propagation
sequenceDiagram autonumber participant Steam as Steam / Master Process participant Ext as ExternalReplayer participant CLI as fossilize_replay (Worker) participant VK as Vulkan Instance Steam->>Ext: Options { device_pci_vendor: 0x10de, device_pci_device: 0x2488 } alt Linux Platform Ext->>CLI: execv("--device-pci-vendor 0x10de --device-pci-device 0x2488") else Windows Platform Ext->>CLI: CreateProcess(cmdline with "0x10de" hex formatting) end CLI->>VK: vkEnumeratePhysicalDevices() VK-->>CLI: Returns GPU list [0: iGPU (0x8086), 1: dGPU (0x10de)] CLI->>CLI: Deterministic match: vendorID == 0x10de && deviceID == 0x2488 CLI->>VK: vkCreateDevice(Selected GPU #1) Note over CLI, VK: Replay shaders strictly on target dGPU (Zero iGPU overhead)Verification & CTest Coverage
orphan-prevention-test: Validates process lifecycle signal delivery and race-freegetppid()checking.gpu-selection-test: Validates GPU selection options structure and vendor ID filtering.Systems Case Study & Root Cause Analysis (RCA)
For full architectural details, container namespace analysis, cross-platform job object parity breakdown, and extended code analysis, see the published RCA Case Study: