Skip to content

Commit 14e79a2

Browse files
committed
fix bugs when torchrun fail main process exit but remain some zombie processes keep using gpu
1 parent 0d10840 commit 14e79a2

1 file changed

Lines changed: 35 additions & 19 deletions

File tree

fastvideo/tests/distributed/test_ring_attention.py

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# If the current environment has known NCCL P2P / IPC issues (e.g., some Docker
1111
# containers), the test can be launched with NCCL socket fallback:
1212
#
13-
# CUDA_VISIBLE_DEVICES=0,1 \
13+
# CUDA_VISIBLE_DEVICES=0,1,2,3 \
1414
# NCCL_CUMEM_ENABLE=0 \
1515
# NCCL_CUMEM_HOST_ENABLE=0 \
1616
# NCCL_P2P_DISABLE=1 \
@@ -706,24 +706,36 @@ def _run_torchrun(
706706
)
707707

708708
try:
709-
# Do not capture stdout/stderr: per-rank progress logs must remain
710-
# visible so a communication hang can be located immediately.
711-
returncode = proc.wait(timeout=120)
712-
except subprocess.TimeoutExpired as exc:
713-
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
709+
try:
710+
# Do not capture stdout/stderr: per-rank progress logs must
711+
# remain visible so a communication hang can be located
712+
# immediately.
713+
returncode = proc.wait(timeout=120)
714+
except subprocess.TimeoutExpired as exc:
715+
raise RuntimeError(
716+
"The multi-GPU Ring Attention worker timed out after 120 "
717+
"seconds and its process group was killed. Use the last "
718+
"printed per-rank stage to determine whether the hang "
719+
"occurred during initialization, Ring P2P communication, "
720+
"all_gather, or the final barrier."
721+
) from exc
722+
723+
if returncode != 0:
724+
raise RuntimeError(
725+
f"Ring Attention worker exited with code {returncode}."
726+
)
727+
finally:
728+
# torchrun exiting -- whether cleanly, with a nonzero code, or via
729+
# the timeout above -- does not guarantee every rank worker in its
730+
# process group has exited. A rank stuck in a NCCL collective after
731+
# a sibling crashes can outlive the launcher, reparent to init, and
732+
# keep pegging the GPUs for every test that runs afterward. Always
733+
# sweep the whole session's process group before returning control.
734+
try:
735+
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
736+
except ProcessLookupError:
737+
pass
714738
proc.wait()
715-
raise RuntimeError(
716-
"The multi-GPU Ring Attention worker timed out after 120 seconds "
717-
"and its process group was killed. Use the last printed per-rank "
718-
"stage to determine whether the hang occurred during "
719-
"initialization, Ring P2P communication, all_gather, or the "
720-
"final barrier."
721-
) from exc
722-
723-
if returncode != 0:
724-
raise RuntimeError(
725-
f"Ring Attention worker exited with code {returncode}."
726-
)
727739

728740

729741
@pytest.mark.parametrize(
@@ -744,7 +756,11 @@ def test_torchrun_debug_defaults_are_opt_in(
744756
captured_env: dict[str, str] = {}
745757

746758
class _CompletedProcess:
747-
pid = 1
759+
# A pid that cannot correspond to a real process: _run_torchrun's
760+
# cleanup now unconditionally calls os.getpgid()/os.killpg() on this
761+
# pid in a finally block, and this test must not let that touch a
762+
# real process group (e.g. pid 1's).
763+
pid = 999999999
748764

749765
@staticmethod
750766
def wait(timeout: int | None = None) -> int:

0 commit comments

Comments
 (0)