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