Repository navigation
[training] fix: install OS signal trap by entering DistributedSignalHandler - #3823
Conversation
…andler Signed-off-by: Robert Luke <code@robertluke.net>
|
/claude review |
| """Initializes the distributed signal handler based on the configuration.""" | ||
| if self.cfg.train is not None: | ||
| self._signal_handler = DistributedSignalHandler(self.cfg.train.exit_signal) | ||
| self._signal_handler = DistributedSignalHandler(self.cfg.train.exit_signal).__enter__() |
There was a problem hiding this comment.
Bug: reset_for_restart (line 494) sets self._signal_handler = None without calling self._signal_handler.release() first. Before this PR that was harmless because __enter__() was never called, so no OS trap was installed. Now that the trap IS installed, discarding the handler without release() orphans the OS-level signal handler — the original SIGTERM handler is never restored, and on restart a new handler stacks on top of the leaked one.
Suggested fix — guard the teardown in reset_for_restart:
if self._signal_handler is not None:
self._signal_handler.release()
self._signal_handler = NoneThere was a problem hiding this comment.
Good suggestion. Suggested fix has been applied.
|
@rob-luke can you check ai comment? |
Code ReviewBug: leaked OS signal handler on reset_for_restart reset_for_restart (state.py:494) sets self._signal_handler = None without calling release() first. Before this PR that was harmless -- enter() was never called, so no OS trap existed. Now that the trap IS installed, discarding the handler orphans the OS-level SIGTERM handler: the original handler is never restored, and on in-process restart a new handler stacks on top of the leaked one. Suggested fix in reset_for_restart: (Also left an inline comment on the same line.) Existing mock test does not verify the fix test_set_signal_handler_with_train_config (test_state.py:631-648) still passes only because MagicMock.enter() returns self by default -- the assertion state._signal_handler == mock_signal_handler is incidentally correct. Reverting the enter() call in state.py would NOT break this test. Consider adding: so the mock-based test actually guards the fix. New tests look good The two new regression tests (test_set_signal_handler_installs_os_trap, test_sigterm_flips_signal_received_flag) are well-written -- the restore_sigterm_handler fixture properly isolates signal state, the safety-net no-op handler in the SIGTERM test prevents process death on regression, and the end-to-end test drives the public cfg setter path. Suggested test cases No perf tests impacted. |
Signed-off-by: Robert Luke <code@robertluke.net>
|
@yaoyu-33 the feedback from ai was good. I have implemented the suggested change. This PR is ready for review again. |
|
@kamran-nvidia and @yaoyu-33 this issue is tagged as waiting on customer, but i think I addressed the earlier concerns. Do you have further changes you'd like me to make? Thank you for your help and the great package. |
skills/review-pr/SKILL.md (added in NVIDIA-NeMo#4142) was copied into docs/skills/ by conf.py but never referenced in any toctree, so the docs build failed with "document isn't included in any toctree" under --fail-on-warning. Add it to the Getting Started toctree alongside the other contributor workflow skills (cicd, testing). Signed-off-by: Robert Luke <code@robertluke.net>
|
Thank you @adityavavreNVDA , @JRD971000 , @yaoyu-33. I think the ci failure was unrelated to the PR and occurred when I merged main to the branch, a skill was missing from the docs index. I’ve resolved the issue by adding the skill to the doc page, but can revert if the fix lands in main. |
|
/ok to test 3af3319 |
|
Thank you @adityavavreNVDA for running the tests. Please let me know if there are additional changes you'd like to the PR. |
|
Thank you @yaoyu-33 & @adityavavreNVDA |
…andler (NVIDIA-NeMo#3823) Signed-off-by: Robert Luke <code@robertluke.net> Co-authored-by: adityavavreNVDA <avavre@nvidia.com> Signed-off-by: Vasudevan Rengasamy <vrengasamy@nvidia.com>
What does this PR do ?
Install the OS-level SIGTERM trap so
cfg.train.exit_signal_handler=Truecatches signals on the main process.Changelog
src/megatron/bridge/training/state.py: call.__enter__()on theDistributedSignalHandlerconstructed inGlobalState._set_signal_handlersosignal.signal(SIGTERM, handler)runs. After this, sending SIGTERM to the main process flipssignal_handler._signal_received, and the polled exit branch attrain.py:~1320triggers a graceful checkpoint-and-exit.tests/unit_tests/training/test_state.py: add two unit tests using the realDistributedSignalHandler— one assertssignal.getsignal(SIGTERM)changes after_set_signal_handler, the other sends a real SIGTERM viaos.killand asserts_signal_receivedflips. A class-scoped fixture snapshots and restoressignal.getsignal(SIGTERM)so the installed trap does not leak across tests.GitHub Actions CI
See the CI section in the Contributing doc for how to trigger the CI. A Nvidia developer will need to approve and trigger the CI for external contributors.
Before your PR is "Ready for review"
Pre checks:
Additional Information