mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Added stop_event to util.Process (#14142)
* Added stop_event to util.Process util.Process will take care of receiving signals when the stop_event is accessed in the subclass. If it never is, SystemExit is raised instead. This has the effect of still behaving like multiprocessing.Process when stop_event is not accessed, while still allowing subclasses to not deal with the hassle of setting it up. * Give each util.Process their own logger This will help to reduce boilerplate in subclasses. * Give explicit types to util.Process.__init__ This gives better type hinting in the editor. * Use util.Process facilities in AudioProcessor Boilerplate begone! * Removed pointless check in util.Process The log_listener.queue should never be None, unless something has gone extremely wrong in the log setup code. If we're that far gone, crashing is better. * Make sure faulthandler is enabled in all processes This has no effect currently since we're using the fork start_method. However, when we inevidably switch to forkserver (either by choice, or by upgrading to python 3.14+) not having this makes for some really fun failure modes :D
This commit is contained in:
		
							parent
							
								
									e725730982
								
							
						
					
					
						commit
						a468ed316d
					
				| @ -2,8 +2,6 @@ | |||||||
| 
 | 
 | ||||||
| import datetime | import datetime | ||||||
| import logging | import logging | ||||||
| import signal |  | ||||||
| import sys |  | ||||||
| import threading | import threading | ||||||
| import time | import time | ||||||
| from typing import Tuple | from typing import Tuple | ||||||
| @ -73,36 +71,31 @@ class AudioProcessor(util.Process): | |||||||
|     ): |     ): | ||||||
|         super().__init__(name="frigate.audio_manager", daemon=True) |         super().__init__(name="frigate.audio_manager", daemon=True) | ||||||
| 
 | 
 | ||||||
|         self.logger = logging.getLogger(self.name) |  | ||||||
|         self.camera_metrics = camera_metrics |         self.camera_metrics = camera_metrics | ||||||
|         self.cameras = cameras |         self.cameras = cameras | ||||||
| 
 | 
 | ||||||
|     def run(self) -> None: |     def run(self) -> None: | ||||||
|         stop_event = threading.Event() |  | ||||||
|         audio_threads: list[AudioEventMaintainer] = [] |         audio_threads: list[AudioEventMaintainer] = [] | ||||||
| 
 | 
 | ||||||
|         threading.current_thread().name = "process:audio_manager" |         threading.current_thread().name = "process:audio_manager" | ||||||
|         signal.signal(signal.SIGTERM, lambda sig, frame: sys.exit()) |  | ||||||
| 
 | 
 | ||||||
|         if len(self.cameras) == 0: |         if len(self.cameras) == 0: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         try: |  | ||||||
|         for camera in self.cameras: |         for camera in self.cameras: | ||||||
|             audio_thread = AudioEventMaintainer( |             audio_thread = AudioEventMaintainer( | ||||||
|                 camera, |                 camera, | ||||||
|                 self.camera_metrics, |                 self.camera_metrics, | ||||||
|                     stop_event, |                 self.stop_event, | ||||||
|             ) |             ) | ||||||
|             audio_threads.append(audio_thread) |             audio_threads.append(audio_thread) | ||||||
|             audio_thread.start() |             audio_thread.start() | ||||||
| 
 | 
 | ||||||
|         self.logger.info(f"Audio processor started (pid: {self.pid})") |         self.logger.info(f"Audio processor started (pid: {self.pid})") | ||||||
| 
 | 
 | ||||||
|             while True: |         while not self.stop_event.wait(): | ||||||
|                 signal.pause() |             pass | ||||||
|         finally: | 
 | ||||||
|             stop_event.set() |  | ||||||
|         for thread in audio_threads: |         for thread in audio_threads: | ||||||
|             thread.join(1) |             thread.join(1) | ||||||
|             if thread.is_alive(): |             if thread.is_alive(): | ||||||
| @ -112,6 +105,7 @@ class AudioProcessor(util.Process): | |||||||
|         for thread in audio_threads: |         for thread in audio_threads: | ||||||
|             if thread.is_alive(): |             if thread.is_alive(): | ||||||
|                 self.logger.warning(f"Thread {thread.name} is still alive") |                 self.logger.warning(f"Thread {thread.name} is still alive") | ||||||
|  | 
 | ||||||
|         self.logger.info("Exiting audio processor") |         self.logger.info("Exiting audio processor") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,15 +1,29 @@ | |||||||
|  | import faulthandler | ||||||
| import logging | import logging | ||||||
| import multiprocessing as mp | import multiprocessing as mp | ||||||
|  | import signal | ||||||
|  | import sys | ||||||
|  | import threading | ||||||
| from functools import wraps | from functools import wraps | ||||||
| from logging.handlers import QueueHandler | from logging.handlers import QueueHandler | ||||||
| from typing import Any | from typing import Any, Callable, Optional | ||||||
| 
 | 
 | ||||||
| import frigate.log | import frigate.log | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class BaseProcess(mp.Process): | class BaseProcess(mp.Process): | ||||||
|     def __init__(self, **kwargs): |     def __init__( | ||||||
|         super().__init__(**kwargs) |         self, | ||||||
|  |         *, | ||||||
|  |         name: Optional[str] = None, | ||||||
|  |         target: Optional[Callable] = None, | ||||||
|  |         args: tuple = (), | ||||||
|  |         kwargs: dict = {}, | ||||||
|  |         daemon: Optional[bool] = None, | ||||||
|  |     ): | ||||||
|  |         super().__init__( | ||||||
|  |             name=name, target=target, args=args, kwargs=kwargs, daemon=daemon | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     def start(self, *args, **kwargs): |     def start(self, *args, **kwargs): | ||||||
|         self.before_start() |         self.before_start() | ||||||
| @ -46,10 +60,36 @@ class BaseProcess(mp.Process): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Process(BaseProcess): | class Process(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: |     def before_start(self) -> None: | ||||||
|         self.__log_queue = frigate.log.log_listener.queue |         self.__log_queue = frigate.log.log_listener.queue | ||||||
| 
 | 
 | ||||||
|     def before_run(self) -> None: |     def before_run(self) -> None: | ||||||
|         if self.__log_queue: |         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) | ||||||
|  | 
 | ||||||
|  |         self.logger = logging.getLogger(self.name) | ||||||
|  | 
 | ||||||
|         logging.basicConfig(handlers=[], force=True) |         logging.basicConfig(handlers=[], force=True) | ||||||
|         logging.getLogger().addHandler(QueueHandler(self.__log_queue)) |         logging.getLogger().addHandler(QueueHandler(self.__log_queue)) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user