Skip to content

[training] fix: install OS signal trap by entering DistributedSignalHandler - #3823

Merged
adityavavreNVDA merged 9 commits into
NVIDIA-NeMo:mainfrom
rob-luke:rob-luke/training/signal-handler-enter
Jun 9, 2026
Merged

adityavavreNVDA merged 9 commits into
NVIDIA-NeMo:mainfrom
rob-luke:rob-luke/training/signal-handler-enter

Conversation

@rob-luke

Copy link
Copy Markdown
Contributor

What does this PR do ?

Install the OS-level SIGTERM trap so cfg.train.exit_signal_handler=True catches signals on the main process.

Changelog

  • src/megatron/bridge/training/state.py: call .__enter__() on the DistributedSignalHandler constructed in GlobalState._set_signal_handler so signal.signal(SIGTERM, handler) runs. After this, sending SIGTERM to the main process flips signal_handler._signal_received, and the polled exit branch at train.py:~1320 triggers a graceful checkpoint-and-exit.
  • tests/unit_tests/training/test_state.py: add two unit tests using the real DistributedSignalHandler — one asserts signal.getsignal(SIGTERM) changes after _set_signal_handler, the other sends a real SIGTERM via os.kill and asserts _signal_received flips. A class-scoped fixture snapshots and restores signal.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:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you add or update any necessary documentation?
  • Does the PR affect components that are optional to install? (Ex: Numba, Pynini, Apex etc)

Additional Information

  • Related to # (issue) — none filed

…andler

Signed-off-by: Robert Luke <code@robertluke.net>
@copy-pr-bot

copy-pr-bot Bot commented May 14, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@rob-luke rob-luke changed the title [training] fix: install OS signal trap by entering DistributedSignalHandler fix(training): install OS signal trap by entering DistributedSignalHandler May 14, 2026
@rob-luke rob-luke changed the title fix(training): install OS signal trap by entering DistributedSignalHandler [training] fix: install OS signal trap by entering DistributedSignalHandler May 14, 2026
@yaoyu-33 yaoyu-33 added area:training Training loop, callbacks, and runtime integration bug Something isn't working waiting-on-maintainers Waiting on maintainers to respond labels May 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-maintainers Waiting on maintainers to respond label May 14, 2026
@yaoyu-33 yaoyu-33 added the needs-review PR is ready for code review and waiting on a reviewer label May 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label May 16, 2026
@yaoyu-33 yaoyu-33 removed the needs-review PR is ready for code review and waiting on a reviewer label May 17, 2026
@yaoyu-33

Copy link
Copy Markdown
Contributor

/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__()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = None

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. Suggested fix has been applied.

@yaoyu-33

Copy link
Copy Markdown
Contributor

@rob-luke can you check ai comment?

@yaoyu-33 yaoyu-33 added waiting-on-customer Waiting on the original author to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels May 20, 2026
@claude

claude Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Code Review

Bug: 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:

if self._signal_handler is not None:
    self._signal_handler.release()
self._signal_handler = None

(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:

mock_signal_handler.__enter__.assert_called_once()

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.

@rob-luke

Copy link
Copy Markdown
Contributor Author

@yaoyu-33 the feedback from ai was good. I have implemented the suggested change. This PR is ready for review again.

@rob-luke

Copy link
Copy Markdown
Contributor Author

@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.

@kamran-nvidia kamran-nvidia added needs-review PR is ready for code review and waiting on a reviewer and removed waiting-on-customer Waiting on the original author to respond labels May 27, 2026
@kamran-nvidia
kamran-nvidia requested a review from yaoyu-33 May 27, 2026 00:09
yaoyu-33
yaoyu-33 previously approved these changes May 28, 2026
@yaoyu-33 yaoyu-33 added ready-to-merge PR is approved, current, and only waiting for CI to pass before merge and removed needs-review PR is ready for code review and waiting on a reviewer labels May 28, 2026
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>
@rob-luke

rob-luke commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

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.

@adityavavreNVDA

Copy link
Copy Markdown
Contributor

/ok to test 3af3319

@rob-luke

rob-luke commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @adityavavreNVDA for running the tests. Please let me know if there are additional changes you'd like to the PR.

@yaoyu-33 yaoyu-33 added ready-to-merge PR is approved, current, and only waiting for CI to pass before merge and removed waiting-on-maintainers Waiting on maintainers to respond labels Jun 9, 2026
@adityavavreNVDA
adityavavreNVDA merged commit 29868ca into NVIDIA-NeMo:main Jun 9, 2026
93 of 100 checks passed
@rob-luke
rob-luke deleted the rob-luke/training/signal-handler-enter branch June 9, 2026 19:50
@rob-luke

rob-luke commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @yaoyu-33 & @adityavavreNVDA

vasunvidia pushed a commit to vasunvidia/Megatron-Bridge that referenced this pull request Jun 10, 2026
…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>

This branch was previously deployed

2 inactive deployments
public — 3af3319d Deployed Jun 8, 2026 by copy-pr-bot[bot] via release / finalize / publish-docs #1169
test — 3af3319d Deployed Jun 6, 2026 by copy-pr-bot[bot] via cicd-wait-in-queue #15854
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:training Training loop, callbacks, and runtime integration bug Something isn't working community-request ready-to-merge PR is approved, current, and only waiting for CI to pass before merge Run CICD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants