Skip to content

Commit 9ecfac3

Browse files
authored
Merge branch 'main' into anima-bundled-t5-tokenizer
2 parents bbd2930 + db0b08b commit 9ecfac3

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

invokeai/app/services/model_install/model_install_default.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def __init__(
112112
self._stop_event = threading.Event()
113113
self._downloads_changed_event = threading.Event()
114114
self._install_completed_event = threading.Event()
115+
self._restore_completed_event = threading.Event()
116+
self._restore_completed_event.set()
115117
self._download_queue = download_queue
116118
self._download_cache: Dict[int, ModelInstallJob] = {}
117119
self._running = False
@@ -264,16 +266,23 @@ def _restore_incomplete_installs(self) -> None:
264266
self._safe_rmtree(job._install_tmpdir, self._logger)
265267

266268
def _restore_incomplete_installs_async(self) -> None:
269+
self._restore_completed_event.clear()
270+
267271
def _run() -> None:
268272
try:
269273
self._logger.info("Restoring incomplete installs")
270274
self._restore_incomplete_installs()
271275
self._logger.info("Finished restoring incomplete installs")
272276
except Exception as e:
273277
self._logger.error(f"Failed to restore incomplete installs: {e}")
278+
finally:
279+
self._restore_completed_event.set()
274280

275281
threading.Thread(target=_run, daemon=True).start()
276282

283+
def _wait_for_restore_complete(self) -> None:
284+
self._restore_completed_event.wait()
285+
277286
def _resume_remote_download(self, job: ModelInstallJob) -> None:
278287
job.status = InstallStatus.WAITING
279288
if job.download_parts:
@@ -459,6 +468,8 @@ def heuristic_import(
459468
return self.import_model(source_obj, config)
460469

461470
def import_model(self, source: ModelSource, config: Optional[ModelRecordChanges] = None) -> ModelInstallJob: # noqa D102
471+
self._wait_for_restore_complete()
472+
462473
similar_jobs = [x for x in self.list_jobs() if x.source == source and not x.in_terminal_state]
463474
if similar_jobs:
464475
self._logger.warning(f"There is already an active install job for {source}. Not enqueuing.")
@@ -506,6 +517,8 @@ def wait_for_job(self, job: ModelInstallJob, timeout: int = 0) -> ModelInstallJo
506517

507518
def wait_for_installs(self, timeout: int = 0) -> List[ModelInstallJob]: # noqa D102
508519
"""Block until all installation jobs are done."""
520+
self._wait_for_restore_complete()
521+
509522
start = time.time()
510523
while len(self._download_cache) > 0:
511524
if self._downloads_changed_event.wait(timeout=0.25): # in case we miss an event

tests/app/services/model_install/test_model_install.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import gc
66
import platform
77
import shutil
8+
import threading
9+
import time
810
import uuid
911
from pathlib import Path
1012
from typing import Any, Dict
@@ -321,6 +323,55 @@ def test_simple_download(mm2_installer: ModelInstallServiceBase, mm2_app_config:
321323
assert isinstance(bus.events[4], ModelInstallCompleteEvent) # install completed
322324

323325

326+
def test_import_waits_for_startup_restore(
327+
mm2_app_config: InvokeAIAppConfig,
328+
mm2_record_store,
329+
mm2_download_queue,
330+
mm2_session,
331+
embedding_file: Path,
332+
monkeypatch: pytest.MonkeyPatch,
333+
) -> None:
334+
installer = ModelInstallService(
335+
app_config=mm2_app_config,
336+
record_store=mm2_record_store,
337+
download_queue=mm2_download_queue,
338+
event_bus=TestEventService(),
339+
session=mm2_session,
340+
)
341+
restore_started = threading.Event()
342+
release_restore = threading.Event()
343+
imported = threading.Event()
344+
345+
def _blocked_restore() -> None:
346+
restore_started.set()
347+
assert release_restore.wait(timeout=5)
348+
349+
monkeypatch.setattr(installer, "_restore_incomplete_installs", _blocked_restore)
350+
351+
try:
352+
installer.start()
353+
assert restore_started.wait(timeout=5)
354+
355+
import_thread = threading.Thread(
356+
target=lambda: (
357+
installer.import_model(LocalModelSource(path=embedding_file)),
358+
imported.set(),
359+
)
360+
)
361+
import_thread.start()
362+
363+
time.sleep(0.1)
364+
assert not imported.is_set()
365+
366+
release_restore.set()
367+
import_thread.join(timeout=5)
368+
assert imported.is_set()
369+
installer.wait_for_installs(timeout=5)
370+
finally:
371+
release_restore.set()
372+
installer.stop()
373+
374+
324375
def test_huggingface_blob_url_uses_resolve_download_url(mm2_installer: ModelInstallServiceBase) -> None:
325376
source = URLModelSource(
326377
url=Url("https://huggingface.co/h94/IP-Adapter/blob/main/sdxl_models/ip-adapter.safetensors")

0 commit comments

Comments
 (0)