diff --git a/invokeai/app/run_app.py b/invokeai/app/run_app.py index e9be3032a44..f032ff9ab7a 100644 --- a/invokeai/app/run_app.py +++ b/invokeai/app/run_app.py @@ -9,8 +9,6 @@ def get_app(): def run_app() -> None: """The main entrypoint for the app.""" - import os - from invokeai.frontend.cli.arg_parser import InvokeAIArgs # Parse the CLI arguments before doing anything else, which ensures CLI args correctly override settings from other @@ -106,5 +104,8 @@ def run_app() -> None: loop.run_until_complete(server.serve()) except KeyboardInterrupt: logger.info("InvokeAI shutting down...") - # Force exit to avoid hanging on non-daemon background threads (e.g. model install service). - os._exit(0) + # Gracefully shut down services (e.g. model download and install managers) so that any + # active work is completed or cleanly cancelled before the process exits. + from invokeai.app.api.dependencies import ApiDependencies + + ApiDependencies.shutdown() diff --git a/invokeai/app/services/download/download_default.py b/invokeai/app/services/download/download_default.py index f65f00e2aee..7c7fe82124d 100644 --- a/invokeai/app/services/download/download_default.py +++ b/invokeai/app/services/download/download_default.py @@ -88,7 +88,7 @@ def stop(self, *args: Any, **kwargs: Any) -> None: """Stop the download worker threads.""" with self._lock: if not self._worker_pool: - raise Exception("Attempt to stop the download service before it was started") + return self._accept_download_requests = False # reject attempts to add new jobs to queue queued_jobs = [x for x in self.list_jobs() if x.status == DownloadJobStatus.WAITING] active_jobs = [x for x in self.list_jobs() if x.status == DownloadJobStatus.RUNNING] diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index c1fb0c651f4..2726f3e5a91 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -313,7 +313,7 @@ def start(self, invoker: Optional[Invoker] = None) -> None: def stop(self, invoker: Optional[Invoker] = None) -> None: """Stop the installer thread; after this the object can be deleted and garbage collected.""" if not self._running: - raise Exception("Attempt to stop the install service before it was started") + return self._logger.debug("calling stop_event.set()") self._stop_event.set() self._clear_pending_jobs()