Conversation
The GPU worker pickled its offload registration with the "file_system" sharing strategy. Its CPU input buffers were created with share_memory_() under the default "file_descriptor" strategy, so pickling them under "file_system" moved their storage into a new segment owned by a torch_shm_manager, and from then on the GPU worker and the offload process both released that storage through the manager. When the engine is torn down, the manager can exit first. libshm then throws std::system_error (EPIPE) inside the storage destructor and the process dies with "terminate called after throwing an instance of 'std::system_error' what(): Broken pipe" (SIGABRT, core dump) instead of exiting. Depending on the order, the offload worker or the GPU worker aborted. Serialize the registration with "file_descriptor" instead. The buffers keep their storage, no manager is started, and each process releases its mapping locally. Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Peuqui <peuqui@github.com>
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.
Purpose
Unloading a model that runs the PLE offload worker (
VLLM_PLE_CPU_OFFLOAD)regularly ends in an abort instead of a clean exit:
The process dies with SIGABRT and leaves a core dump; on Ubuntu, apport turns
each one into a crash report. It happens in the offload worker or in a GPU
worker, depending on the teardown order.
Cause:
PleOffloadConnector._register_with_offload_workerpickles theregistration with the
file_systemsharing strategy. The CPU input buffers werecreated with
share_memory_()under the defaultfile_descriptorstrategy, sopickling them under
file_systemmoves their storage into a new segment ownedby a
torch_shm_manager(the existing comment "ForkingPickler may replace CPUstorage while converting its sharing strategy" refers to this). From then on the
GPU worker and the offload process both release that storage through the
manager. On teardown the manager can exit first; libshm then throws
std::system_error(EPIPE) inside the storage destructor, which callsstd::terminate.Fix: serialize the registration with
file_descriptor(new helper_dump_registration). The buffers keep their storage, no manager is started,and every process releases its own mapping. CUDA tensors in the registration
still travel through CUDA IPC; nothing else changes.
Why this is not a duplicate:
gh pr list --repo 1CatAI/1Cat-vLLM --state openwith the searches "PLE offload", "PleOffload", "Broken pipe", "sharing
strategy", "file_system", "torch_shm_manager" and "SIGABRT" returns only my
own #646, which does not touch the sharing strategy (it adds
remote_placementsto the same registration, a separate hunk). No issuereports this crash; #530 is about PLE offload GPU memory, not teardown.
Test Plan
On
origin/main(b711d53) plus this change:Two new CPU-only tests:
test_ple_registration_keeps_input_storage_without_shm_manager: after_dump_registrationthe input buffer still has the samedata_ptr, notorch_shm_managerchild exists, and the global sharing strategy is restored.test_ple_registration_outlives_its_sender: a spawned sender registers sharedinput buffers, the receiver reads a value the sender writes afterwards, the
sender is killed with SIGKILL, and the receiver releases the buffers and exits
with code 0. The child imports the vllm under test via
PYTHONPATH, since ascript's
sys.pathstarts at its own directory.Counter-check: with
file_systemrestored in_dump_registration, both newtests fail (the
data_ptrchanges;managers=1).End to end:
nvidia/Qwen3.8-Flash-Next-NVFP4with MTP, TP2 x PP2 on2x Quadro RTX 8000 + 2x V100-PCIE-32GB, PLE offload worker enabled, loaded and
unloaded by llama-swap (one request per load, then unload). This ran on my fork,
which carries this exact change on top of other open work (#646 and its
dependencies); the touched function is identical to
main.Test Result
tests/v1/worker/test_ple_offload_worker.py: 29 passed (27 existing, 2 new).file_system: the 2 new tests fail as expected.mypy-3.10 --hook-stage manual: passed.offload worker both times). With an interim workaround that only skipped the
destructors in the offload worker, the GPU worker
Worker_PP0_TP0stillaborted in 2 of 3 unloads, so both sides are affected.
terminate calledin thelog, no crash report. The three
torch_*segments per load that the deadmanager used to leave in
/dev/shmare gone. (Two other smalltorch_*segments per load from the GPU workers remain; they predate this change and
are unrelated to PLE.)
AI assistance (Claude) was used to find the cause, write the fix and tests, run
the measurements and write this description. I reviewed every changed line and
ran the tests and the end-to-end check on the hardware named above.
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.🤖 Generated with Claude Code