feat: auto-checkpoint on SIGTERM/SIGINT with DDP support
- Register SIGTERM/SIGINT handlers in training loop, set stop flag on signal - Check stop_requested at each epoch/batch boundary, break and call on_error to save checkpoint - LocalStrategy parent forwards signal to child processes via terminate(), waits up to 600s for graceful exit - TrainContext gains threading.Event-based stop_requested/request_stop - Tests verify SIGTERM/SIGINT trigger checkpoint save with exit code 0, works on both CPU and GPU
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import threading
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_early_stop = threading.Event()
|
||||
_active_context = None
|
||||
|
||||
|
||||
def _early_handler(signum: int, frame):
|
||||
sig = signal.Signals(signum)
|
||||
logger.warning(
|
||||
"Received %s (pid=%d), requesting graceful training stop...",
|
||||
sig.name,
|
||||
os.getpid(),
|
||||
)
|
||||
_early_stop.set()
|
||||
if _active_context is not None:
|
||||
_active_context.request_stop()
|
||||
|
||||
|
||||
def install_early_signal_handlers():
|
||||
for sig in (signal.SIGTERM, signal.SIGINT):
|
||||
signal.signal(sig, _early_handler)
|
||||
|
||||
|
||||
def register_signal_handlers(context):
|
||||
global _active_context
|
||||
_active_context = context
|
||||
for sig in (signal.SIGTERM, signal.SIGINT):
|
||||
signal.signal(sig, _early_handler)
|
||||
if _early_stop.is_set():
|
||||
context.request_stop()
|
||||
logger.warning("Signal was received during initialization, stopping...")
|
||||
|
||||
|
||||
def unregister_signal_handlers():
|
||||
global _active_context
|
||||
_active_context = None
|
||||
_early_stop.clear()
|
||||
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
Reference in New Issue
Block a user