From 6ce69c8d35b485f339541ccad4d7d02608a26d2c Mon Sep 17 00:00:00 2001 From: Brett Date: Sun, 29 Oct 2023 22:22:29 -0500 Subject: [PATCH] Thread Safe Downloads for Renderers (#49) * Make engines download on another thread * Fix merge issues --- src/engines/engine_manager.py | 63 +++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/engines/engine_manager.py b/src/engines/engine_manager.py index 83ec0f3..9500bd0 100644 --- a/src/engines/engine_manager.py +++ b/src/engines/engine_manager.py @@ -13,6 +13,7 @@ logger = logging.getLogger() class EngineManager: engines_path = None + download_tasks = [] @staticmethod def supported_engines(): @@ -116,16 +117,34 @@ class EngineManager: return None @classmethod - def download_engine(cls, engine, version, system_os=None, cpu=None): - existing_download = cls.is_version_downloaded(engine, version, system_os, cpu) - if existing_download: - logger.info(f"Requested download of {engine} {version}, but local copy already exists") - return existing_download + def is_already_downloading(cls, engine, version, system_os=None, cpu=None): + for task in cls.download_tasks: + task_parts = task.name.split('-') + task_engine, task_version, task_system_os, task_cpu = task_parts[:4] + + if engine == task_engine and version == task_version: + if system_os in (task_system_os, None) and cpu in (task_cpu, None): + return task + return False + + @classmethod + def download_engine(cls, engine, version, system_os=None, cpu=None, background=False): + def download_engine_task(engine, version, system_os=None, cpu=None): + existing_download = cls.is_version_downloaded(engine, version, system_os, cpu) + if existing_download: + logger.info(f"Requested download of {engine} {version}, but local copy already exists") + return existing_download + + # Get the appropriate downloader class based on the engine type + cls.engine_with_name(engine).downloader().download_engine(version, download_location=cls.engines_path, + system_os=system_os, cpu=cpu, timeout=300) - # Check if the provided engine type is valid engine_to_download = cls.engine_with_name(engine) - if not engine_to_download: - logger.error("No valid engine found") + existing_task = cls.is_already_downloading(engine, version, system_os, cpu) + if existing_task: + logger.debug(f"Already downloading {engine} {version}") + if not background: + existing_task.join() # If download task exists, wait until its done downloading return elif not engine_to_download.downloader(): logger.warning("No valid downloader for this engine. Please update this software manually.") @@ -133,15 +152,19 @@ class EngineManager: elif not cls.engines_path: raise FileNotFoundError("Engines path must be set before requesting downloads") - # Get the appropriate downloader class based on the engine type - engine_to_download.downloader().download_engine(version, download_location=cls.engines_path, - system_os=system_os, cpu=cpu, timeout=300) + thread = threading.Thread(target=download_engine_task, args=(engine, version, system_os, cpu), + name=f'{engine}-{version}-{system_os}-{cpu}') + cls.download_tasks.append(thread) + thread.start() - # Check that engine was properly downloaded - found_engine = cls.is_version_downloaded(engine, version, system_os, cpu) - if not found_engine: - logger.error(f"Error downloading {engine}") - return found_engine + if background: + return thread + else: + thread.join() + found_engine = cls.is_version_downloaded(engine, version, system_os, cpu) # Check that engine downloaded + if not found_engine: + logger.error(f"Error downloading {engine}") + return found_engine @classmethod @@ -163,9 +186,9 @@ class EngineManager: latest_version = engine.downloader().find_most_recent_version() if latest_version: logger.debug(f"Latest version of {engine.name()} available: {latest_version.get('version')}") - if not cls.is_version_downloaded(engine, latest_version.get('version')): - logger.info(f"Downloading {engine.name()} ({latest_version['version']})") - cls.download_engine(engine=engine.name(), version=latest_version['version']) + if not cls.is_version_downloaded(engine.name(), latest_version.get('version')): + logger.info(f"Downloading latest version of {engine.name()}...") + cls.download_engine(engine=engine.name(), version=latest_version['version'], background=True) else: logger.warning(f"Unable to get check for updates for {engine.name()}") @@ -177,8 +200,6 @@ class EngineManager: threads.append(thread) thread.start() - for thread in threads: # wait to finish - thread.join() @classmethod def create_worker(cls, renderer, input_path, output_path, engine_version=None, args=None, parent=None, name=None):