From 0807bd99b956ee3abf18acc3bec43a87fc8b1530 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 10 Jan 2024 23:04:23 -0500 Subject: [PATCH] Fixed improper closing of resources on exit --- bazarr.py | 21 ++++++++------ bazarr/app/database.py | 10 ++++++- bazarr/app/server.py | 55 +++++++++++++++--------------------- bazarr/app/signalr_client.py | 10 +++++-- bazarr/init.py | 2 ++ bazarr/main.py | 12 +++++--- requirements.txt | 1 + 7 files changed, 64 insertions(+), 47 deletions(-) diff --git a/bazarr.py b/bazarr.py index e0f214f93..f5fc76bb8 100644 --- a/bazarr.py +++ b/bazarr.py @@ -52,22 +52,27 @@ dir_name = os.path.dirname(__file__) def end_child_process(ep): try: - ep.kill() + if os.name != 'nt': + try: + ep.send_signal(signal.SIGINT) + except ProcessLookupError: + pass + else: + import win32api + import win32con + try: + win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, ep.pid) + except KeyboardInterrupt: + pass except: - pass - -def terminate_child_process(ep): - try: ep.terminate() - except: - pass def start_bazarr(): script = [get_python_path(), "-u", os.path.normcase(os.path.join(dir_name, 'bazarr', 'main.py'))] + sys.argv[1:] ep = subprocess.Popen(script, stdout=None, stderr=None, stdin=subprocess.DEVNULL) atexit.register(end_child_process, ep=ep) - signal.signal(signal.SIGTERM, lambda signal_no, frame: terminate_child_process(ep)) + signal.signal(signal.SIGTERM, lambda signal_no, frame: end_child_process(ep)) def check_status(): diff --git a/bazarr/app/database.py b/bazarr/app/database.py index 690bda40b..c2a97987d 100644 --- a/bazarr/app/database.py +++ b/bazarr/app/database.py @@ -5,6 +5,7 @@ import json import logging import os import flask_migrate +import signal from dogpile.cache import make_region from datetime import datetime @@ -12,7 +13,7 @@ from datetime import datetime from sqlalchemy import create_engine, inspect, DateTime, ForeignKey, Integer, LargeBinary, Text, func, text, BigInteger # importing here to be indirectly imported in other modules later from sqlalchemy import update, delete, select, func # noqa W0611 -from sqlalchemy.orm import scoped_session, sessionmaker, mapped_column +from sqlalchemy.orm import scoped_session, sessionmaker, mapped_column, close_all_sessions from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.pool import NullPool @@ -74,11 +75,18 @@ session_factory = sessionmaker(bind=engine) database = scoped_session(session_factory) +def close_database(): + close_all_sessions() + engine.dispose() + + @atexit.register def _stop_worker_threads(): database.remove() +signal.signal(signal.SIGTERM, lambda signal_no, frame: close_database()) + Base = declarative_base() metadata = Base.metadata diff --git a/bazarr/app/server.py b/bazarr/app/server.py index 48db3912a..52d711fbe 100644 --- a/bazarr/app/server.py +++ b/bazarr/app/server.py @@ -13,7 +13,7 @@ from api import api_bp from .ui import ui_bp from .get_args import args from .config import settings, base_url -from .database import database +from .database import close_database from .app import create_app app = create_app() @@ -63,49 +63,40 @@ class Server: self.shutdown() def start(self): + logging.info(f'BAZARR is started and waiting for request on http://{self.server.effective_host}:' + f'{self.server.effective_port}') try: - logging.info(f'BAZARR is started and waiting for request on http://{self.server.effective_host}:' - f'{self.server.effective_port}') - try: - self.server.run() - except Exception: - pass - except KeyboardInterrupt: + self.server.run() + except (KeyboardInterrupt, SystemExit): self.shutdown() + except Exception: + pass def shutdown(self): try: - self.server.close() + stop_file = io.open(os.path.join(args.config_dir, "bazarr.stop"), "w", encoding='UTF-8') except Exception as e: - logging.error(f'BAZARR Cannot stop Waitress: {repr(e)}') + logging.error(f'BAZARR Cannot create stop file: {repr(e)}') else: - database.close() - try: - stop_file = io.open(os.path.join(args.config_dir, "bazarr.stop"), "w", encoding='UTF-8') - except Exception as e: - logging.error(f'BAZARR Cannot create stop file: {repr(e)}') - else: - logging.info('Bazarr is being shutdown...') - stop_file.write(str('')) - stop_file.close() - os._exit(0) + logging.info('Bazarr is being shutdown...') + stop_file.write(str('')) + stop_file.close() + close_database() + self.server.close() + os._exit(0) def restart(self): try: - self.server.close() + restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8') except Exception as e: - logging.error(f'BAZARR Cannot stop Waitress: {repr(e)}') + logging.error(f'BAZARR Cannot create restart file: {repr(e)}') else: - database.close() - try: - restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8') - except Exception as e: - logging.error(f'BAZARR Cannot create restart file: {repr(e)}') - else: - logging.info('Bazarr is being restarted...') - restart_file.write(str('')) - restart_file.close() - os._exit(0) + logging.info('Bazarr is being restarted...') + restart_file.write(str('')) + restart_file.close() + close_database() + self.server.close() + os._exit(0) webserver = Server() diff --git a/bazarr/app/signalr_client.py b/bazarr/app/signalr_client.py index 2f048eb17..b731e09e8 100644 --- a/bazarr/app/signalr_client.py +++ b/bazarr/app/signalr_client.py @@ -340,14 +340,20 @@ def consume_queue(queue): data = queue.popleft() except IndexError: pass + except (KeyboardInterrupt, SystemExit): + break else: dispatcher(data) sleep(0.1) # start both queue consuming threads -threading.Thread(target=consume_queue, args=(sonarr_queue,)).start() -threading.Thread(target=consume_queue, args=(radarr_queue,)).start() +sonarr_queue_thread = threading.Thread(target=consume_queue, args=(sonarr_queue,)) +sonarr_queue_thread.daemon = True +sonarr_queue_thread.start() +radarr_queue_thread = threading.Thread(target=consume_queue, args=(radarr_queue,)) +radarr_queue_thread.daemon = True +radarr_queue_thread.start() # instantiate proper SignalR client sonarr_signalr_client = SonarrSignalrClientLegacy() if get_sonarr_info.version().startswith(('0.', '2.', '3.')) else \ diff --git a/bazarr/init.py b/bazarr/init.py index 9db2b73ef..0a2496df0 100644 --- a/bazarr/init.py +++ b/bazarr/init.py @@ -77,6 +77,8 @@ def is_virtualenv(): # deploy requirements.txt if not args.no_update: try: + if os.name == 'nt': + import win32api, win32con # noqa E401 import lxml, numpy, webrtcvad, setuptools, PIL # noqa E401 except ImportError: try: diff --git a/bazarr/main.py b/bazarr/main.py index 970684d14..c2650aed7 100644 --- a/bazarr/main.py +++ b/bazarr/main.py @@ -1,8 +1,6 @@ # coding=utf-8 import os -import io -import logging from threading import Thread @@ -75,9 +73,15 @@ update_notifier() if not args.no_signalr: if settings.general.use_sonarr: - Thread(target=sonarr_signalr_client.start).start() + sonarr_signalr_thread = Thread(target=sonarr_signalr_client.start) + sonarr_signalr_thread.daemon = True + sonarr_signalr_thread.start() + sonarr_signalr_thread.join() if settings.general.use_radarr: - Thread(target=radarr_signalr_client.start).start() + radarr_signalr_thread = Thread(target=radarr_signalr_client.start) + radarr_signalr_thread.daemon = True + radarr_signalr_thread.start() + radarr_signalr_thread.join() if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index 7e40a4347..469b8808b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ lxml>=4.3.0, <5.0.0 numpy>=1.12.0 webrtcvad-wheels>=2.0.10 Pillow>=9.0.0 --only-binary=Pillow +pywin32; platform_system == "Windows"