Handle SIGINT with forkserver (#18860)

* Pass stopevent from main start

* Share stop event across processes

* preload modules

* remove explicit os._exit call

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
Nicolas Mowen
2025-08-16 10:20:33 -05:00
committed by Blake Blackshear
co-authored by Josh Hawkins
parent e1ee6f010f
commit 542bf05bb8
11 changed files with 68 additions and 46 deletions
+4 -22
View File
@@ -1,10 +1,9 @@
import faulthandler
import logging
import multiprocessing as mp
import signal
import sys
import threading
from logging.handlers import QueueHandler
from multiprocessing.synchronize import Event as MpEvent
from typing import Callable, Optional
from setproctitle import setproctitle
@@ -16,6 +15,7 @@ from frigate.config.logger import LoggerConfig
class BaseProcess(mp.Process):
def __init__(
self,
stop_event: MpEvent,
*,
name: Optional[str] = None,
target: Optional[Callable] = None,
@@ -23,6 +23,7 @@ class BaseProcess(mp.Process):
kwargs: dict = {},
daemon: Optional[bool] = None,
):
self.stop_event = stop_event
super().__init__(
name=name, target=target, args=args, kwargs=kwargs, daemon=daemon
)
@@ -42,14 +43,6 @@ class BaseProcess(mp.Process):
class FrigateProcess(BaseProcess):
logger: logging.Logger
@property
def stop_event(self) -> threading.Event:
# Lazily create the stop_event. This allows the signal handler to tell if anyone is
# monitoring the stop event, and to raise a SystemExit if not.
if "stop_event" not in self.__dict__:
self.__dict__["stop_event"] = threading.Event()
return self.__dict__["stop_event"]
def before_start(self) -> None:
self.__log_queue = frigate.log.log_listener.queue
@@ -58,18 +51,7 @@ class FrigateProcess(BaseProcess):
threading.current_thread().name = f"process:{self.name}"
faulthandler.enable()
def receiveSignal(signalNumber, frame):
# Get the stop_event through the dict to bypass lazy initialization.
stop_event = self.__dict__.get("stop_event")
if stop_event is not None:
# Someone is monitoring stop_event. We should set it.
stop_event.set()
else:
# Nobody is monitoring stop_event. We should raise SystemExit.
sys.exit()
signal.signal(signal.SIGTERM, receiveSignal)
signal.signal(signal.SIGINT, receiveSignal)
# setup logging
self.logger = logging.getLogger(self.name)
logging.basicConfig(handlers=[], force=True)
logging.getLogger().addHandler(QueueHandler(self.__log_queue))