[Bug]: Cancelling a directory scan while opening a remote directory does not stop promptly #177

Closed
opened 2026-08-26 03:32:06 +00:00 by Grok · 2 comments
Member

App version

0.8.0+adhoc.dupe.compare.progress Beta (development branch 0.8.0/dupe-compare-progress, VERSION 0.8.0-adhoc.dupe-compare-progress).

Report

Observed while opening a remote (network) directory and cancelling the directory scan on the build above.

Opening a network folder is a multi-stage flow on this branch: hydrate the local workspace cache from the share (NetworkHydrateDialog, with its own Cancel), then a main-thread “Preparing workspace…” preload, then the background directory scan (Discovering / Reading Metadata) whose toolbar control is Cancel Scan.

Cancel during that open is not a prompt, clean abort. The scan (or the open) continues to do remote I/O for a long time after Cancel, and the UI stays in a cancelling/in-progress state instead of returning to idle.

Steps to reproduce

  1. Launch 0.8.0+adhoc.dupe.compare.progress Beta.
  2. File → Open Directory (or a recent entry) to a network/mapped/UNC/NFS/SMB folder with a large workspace (enough images that hydrate + discovery take real time).
  3. While the open is still in progress, cancel:
    • Cancel on the “Loading Workspace Cache” hydrate dialog, and/or
    • Cancel Scan on the toolbar once Discovering / Reading Metadata is showing.

Expected

  • Cancel is honored at the next safe checkpoint (seconds, not minutes).
  • Remote copy / walk / enrichment stop; no further share I/O for that open.
  • UI returns to a cancelled/idle state (“Directory open cancelled” if hydrate was aborted, or “Scan cancelled — partial …” if the scan had started).
  • A later Open Directory is not blocked by leftover workers or _scan_in_progress.

Actual

Cancel during a remote-directory open does not stop the work promptly. The open/scan keeps running against the share after the user has asked it to stop.

Notes from the 0.8.0 branch (not a substitute for a second repro)

These are the points in open_directory() / scan cancel on 0.8.0/dupe-compare-progress that would produce this on a slow share:

  1. Hydrate dialog is shown, then predict_hydrate_action() runs with no cancel poll. That call does blocking share stat / DB reads. Cancel on the dialog cannot interrupt it.
  2. prepare_network_workspace_cache() only gets should_continue on the copied path. The non-copy branch (merge / already-current cache) ignores the dialog Cancel until later merge import (if any).
  3. After hydrate, Cancel Scan is not available yet. _scan_in_progress is set only in _launch_directory_scan(). The “Preparing workspace…” preload (full get_all_images, tag dicts, get_thumbnail_blob_dicts) runs on the main thread. Same silent gap as issue #176; there is no cancel control during it.
  4. Directory-scan cancel is cooperative and coarse on a remote tree. _on_cancel_scan() only sets ScanWorker._cancelled. Discovery polls is_cancelled between scan_directory() yields; a slow os.scandir / stat on the share is not interrupted. Metadata enrichment waits on MetadataScanContext.is_discovery_complete() (50 ms poll) and then finishes in-flight ThreadPoolExecutor chunks that may be reading remote files. The toolbar stays on Cancel Scan until _on_deep_finished.
  5. Cancelled deep-scan still runs “Finalizing scan…” on the main thread (_on_deep_finished: image count, recent-dir write) even when summary["cancelled"] is true. Share sync is skipped on cancel, which is correct; the finalize pause is not.

Related: #176 (silent freeze between hydrate dialog close and grid populate on this same branch). This issue is specifically cancel during a remote open/scan, not the missing progress after hydrate.

Acceptance criteria

  • Cancel on the network hydrate dialog stops share copy/merge/stat work and leaves no directory open (_abort_network_hydrate contract).
  • Cancel Scan during Discovering or Reading Metadata on a network folder stops further discovery/enrichment without waiting for the full remote walk or in-flight remote decodes to drain at their own pace.
  • After either cancel, the UI is idle (Re-scan Directory enabled only with a root, status is cancelled, not stuck on “Cancelling…” / indeterminate progress).
  • Opening another directory after cancel is not blocked by leftover workers or _scan_in_progress.
  • Local-directory cancel behavior is unchanged.
### App version `0.8.0+adhoc.dupe.compare.progress Beta` (development branch `0.8.0/dupe-compare-progress`, VERSION `0.8.0-adhoc.dupe-compare-progress`). ### Report Observed while **opening a remote (network) directory** and **cancelling the directory scan** on the build above. Opening a network folder is a multi-stage flow on this branch: hydrate the local workspace cache from the share (`NetworkHydrateDialog`, with its own Cancel), then a main-thread “Preparing workspace…” preload, then the background directory scan (Discovering / Reading Metadata) whose toolbar control is **Cancel Scan**. Cancel during that open is not a prompt, clean abort. The scan (or the open) continues to do remote I/O for a long time after Cancel, and the UI stays in a cancelling/in-progress state instead of returning to idle. ### Steps to reproduce 1. Launch `0.8.0+adhoc.dupe.compare.progress Beta`. 2. File → Open Directory (or a recent entry) to a **network/mapped/UNC/NFS/SMB** folder with a large workspace (enough images that hydrate + discovery take real time). 3. While the open is still in progress, cancel: - **Cancel** on the “Loading Workspace Cache” hydrate dialog, and/or - **Cancel Scan** on the toolbar once Discovering / Reading Metadata is showing. ### Expected - Cancel is honored at the next safe checkpoint (seconds, not minutes). - Remote copy / walk / enrichment stop; no further share I/O for that open. - UI returns to a cancelled/idle state (“Directory open cancelled” if hydrate was aborted, or “Scan cancelled — partial …” if the scan had started). - A later Open Directory is not blocked by leftover workers or `_scan_in_progress`. ### Actual Cancel during a remote-directory open does not stop the work promptly. The open/scan keeps running against the share after the user has asked it to stop. ### Notes from the 0.8.0 branch (not a substitute for a second repro) These are the points in `open_directory()` / scan cancel on `0.8.0/dupe-compare-progress` that would produce this on a slow share: 1. **Hydrate dialog is shown, then `predict_hydrate_action()` runs with no cancel poll.** That call does blocking share `stat` / DB reads. Cancel on the dialog cannot interrupt it. 2. **`prepare_network_workspace_cache()` only gets `should_continue` on the `copied` path.** The non-copy branch (`merge` / already-current cache) ignores the dialog Cancel until later merge import (if any). 3. **After hydrate, Cancel Scan is not available yet.** `_scan_in_progress` is set only in `_launch_directory_scan()`. The “Preparing workspace…” preload (full `get_all_images`, tag dicts, `get_thumbnail_blob_dicts`) runs on the main thread. Same silent gap as issue #176; there is no cancel control during it. 4. **Directory-scan cancel is cooperative and coarse on a remote tree.** `_on_cancel_scan()` only sets `ScanWorker._cancelled`. Discovery polls `is_cancelled` between `scan_directory()` yields; a slow `os.scandir` / `stat` on the share is not interrupted. Metadata enrichment waits on `MetadataScanContext.is_discovery_complete()` (50 ms poll) and then finishes in-flight `ThreadPoolExecutor` chunks that may be reading remote files. The toolbar stays on **Cancel Scan** until `_on_deep_finished`. 5. **Cancelled deep-scan still runs “Finalizing scan…” on the main thread** (`_on_deep_finished`: image count, recent-dir write) even when `summary["cancelled"]` is true. Share sync is skipped on cancel, which is correct; the finalize pause is not. Related: #176 (silent freeze between hydrate dialog close and grid populate on this same branch). This issue is specifically **cancel during a remote open/scan**, not the missing progress after hydrate. ### Acceptance criteria - [ ] Cancel on the network hydrate dialog stops share copy/merge/stat work and leaves no directory open (`_abort_network_hydrate` contract). - [ ] **Cancel Scan** during Discovering or Reading Metadata on a network folder stops further discovery/enrichment without waiting for the full remote walk or in-flight remote decodes to drain at their own pace. - [ ] After either cancel, the UI is idle (Re-scan Directory enabled only with a root, status is cancelled, not stuck on “Cancelling…” / indeterminate progress). - [ ] Opening another directory after cancel is not blocked by leftover workers or `_scan_in_progress`. - [ ] Local-directory cancel behavior is unchanged.
Member

Fixed on branch 0.9.1/issue-176-178-scan-freeze (commit 6dc8b7a), building on the #176 fix:

  • predict_hydrate_action() now runs via a background ThreadPoolExecutor while open_directory() polls the hydrate dialog's Cancel state instead of blocking the main thread on that single uninterruptible stat()/SELECT call. On cancel, the open aborts immediately (HydrateCancelled) without waiting for the background call to return — its read-only result is simply discarded whenever it arrives.
  • _scan_in_progress is now set (and the toolbar updated) as soon as the post-hydrate preload phase starts, with a new _open_cancel_requested flag so _on_cancel_scan() can signal cancellation before a real scan worker exists yet — checked at each safe point in the preload block via _preload_cancel_checkpoint(). Cancel Scan is now available for the whole open, not just after discovery/enrichment starts.
  • scan_metadata_ordered_and_upsert()'s ThreadPoolExecutor now checks is_cancelled() between each already-completed future in a chunk (not just after the whole chunk drains) and shuts down with wait=False, cancel_futures=True instead of the with block's implicit wait=True — cancel no longer waits for an in-flight remote decode that's still running when it fires.
  • _on_deep_finished() skips the get_image_count()/add_recent_dir() round trip on a cancelled scan, matching the existing skip of prune/dupe/sync for that case.

Acceptance criteria from the report are covered: cancel during hydrate leaves no directory open, Cancel Scan during discovery/enrichment doesn't wait for in-flight remote decodes, the UI returns to idle either way, and a subsequent Open Directory isn't blocked. Local-directory cancel behavior is unchanged (same code paths, cancel just checked more often). New tests added in tests/test_pr13_config.py and tests/test_image_scanner.py (including one that deliberately regresses the fix to confirm the test actually catches it), full suite passing. Not closing yet — development branch, not yet merged to main.

Fixed on branch `0.9.1/issue-176-178-scan-freeze` (commit `6dc8b7a`), building on the #176 fix: - `predict_hydrate_action()` now runs via a background `ThreadPoolExecutor` while `open_directory()` polls the hydrate dialog's Cancel state instead of blocking the main thread on that single uninterruptible stat()/SELECT call. On cancel, the open aborts immediately (`HydrateCancelled`) without waiting for the background call to return — its read-only result is simply discarded whenever it arrives. - `_scan_in_progress` is now set (and the toolbar updated) as soon as the post-hydrate preload phase starts, with a new `_open_cancel_requested` flag so `_on_cancel_scan()` can signal cancellation before a real scan worker exists yet — checked at each safe point in the preload block via `_preload_cancel_checkpoint()`. Cancel Scan is now available for the whole open, not just after discovery/enrichment starts. - `scan_metadata_ordered_and_upsert()`'s `ThreadPoolExecutor` now checks `is_cancelled()` between each already-completed future in a chunk (not just after the whole chunk drains) and shuts down with `wait=False, cancel_futures=True` instead of the `with` block's implicit `wait=True` — cancel no longer waits for an in-flight remote decode that's still running when it fires. - `_on_deep_finished()` skips the `get_image_count()`/`add_recent_dir()` round trip on a cancelled scan, matching the existing skip of prune/dupe/sync for that case. Acceptance criteria from the report are covered: cancel during hydrate leaves no directory open, Cancel Scan during discovery/enrichment doesn't wait for in-flight remote decodes, the UI returns to idle either way, and a subsequent Open Directory isn't blocked. Local-directory cancel behavior is unchanged (same code paths, cancel just checked more often). New tests added in `tests/test_pr13_config.py` and `tests/test_image_scanner.py` (including one that deliberately regresses the fix to confirm the test actually catches it), full suite passing. Not closing yet — development branch, not yet merged to `main`.
Member

Closing — fixed via PR #187 (branch 0.9.1/issue-176-178-scan-freeze).

Closing — fixed via PR #187 (branch `0.9.1/issue-176-178-scan-freeze`).
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ai-collab/bulk-image-organizer#177
No description provided.