Skip to content

BUG-149 - Shared directory tree rebuild is slow and UI-blocking on large shared sets

Summary

On large shared-directory sets, rebuilding the main-window Shared Files directory filter tree (CSharedDirsTreeCtrl::FilterTreeReloadTree / BuildSharedDirectoryTree) is slow and blocks the UI. Adding or removing a shared directory triggers a full tree rebuild plus a full shared-file reload on the UI thread.

This item covers the low-risk, tree-local quick wins. The dominant cost - the full shared-file re-walk on add - is tracked separately as [BUG-147], and file-list filter cost as [FEAT-028]/[FEAT-034].

Findings

  1. Uncached filesystem probes per rebuild. BuildSharedDirectoryTree (SharedDirsTreeCtrl.cpp) calls IsAccessibleDirectoryForSharedTree (LongPathSeams::DirectoryExists) once per directory, with no caching. The member cache m_mapAccessibleDirectoryCache is wiped at the start of every FilterTreeReloadTree and is not used by this path. Every add/remove/filter rebuild re-stats every shared directory; each stat can stall on network/removable/offline volumes.
  2. Live redraw during rebuild. FilterTreeReloadTree inserts every node (InsertItem + TVI_SORT) without SetRedraw(FALSE), so each insert repaints, relayouts, and re-sorts.
  3. Dead O(n^2) code. FilterTreeAddSubDirectories and FilterTreeIsSubDirectory are unused (only self-recursive) and rescan the whole directory list recomputing path keys per pair - a latent O(n^2) trap.

Fix (quick wins, tree-local)

  • #3 redraw guard: wrap the FilterTreeReloadTree rebuild in SetRedraw(FALSE) ... SetRedraw(TRUE) + Invalidate().
  • #2 persistent accessibility cache: route the builder's accessibility probe through the cached member IsSharedTreeDirectoryAccessible, stop wiping the cache on every rebuild, and invalidate it on OnVolumesChanged (mount/unmount). Unifies the probe on DirectoryExists semantics. Trade-off: a path that loses reachability without a volume event keeps a stale accessibility overlay until the next volume change; acceptable versus N synchronous stats per rebuild.
  • #6 dead-code removal: delete FilterTreeAddSubDirectories and FilterTreeIsSubDirectory plus their header declarations.

The larger add-path full reload (#1) is deferred to [BUG-147].

Acceptance Criteria

  • [ ] Adding/removing a shared directory no longer re-stats every shared directory on each rebuild (probes served from the persistent cache).
  • [ ] Tree rebuild suppresses per-insert repaints.
  • [ ] Volume mount/unmount still refreshes directory accessibility overlays.
  • [ ] Dead filter-tree helpers are removed; no remaining callers.
  • [x] Debug, Release, and diagnostics Release x64 app builds pass before commit.

Resolution

Implemented and shipped in 0.7.3-rc.2. The three tree-local quick-win commits are ancestors of the rc.2 app head 38827709 (tag emulebb-v0.7.3-rc.2): 8ac553c1 (suppress redraw during rebuild), f42ea4c8 (cache shared-dir accessibility across rebuilds), and ce0933a8 (remove dead O(n^2) filter-tree helpers) in srchybrid/SharedDirsTreeCtrl.cpp. The rebuild is now wrapped in SetRedraw(FALSE), accessibility probes are served from the persistent member cache (invalidated on OnVolumesChanged), and the unused FilterTreeAddSubDirectories / FilterTreeIsSubDirectory helpers are gone. The larger add-path full reload remains tracked separately as BUG-147 (deferred post-0.7.3). Build gates (x64 Debug/Release + diagnostics Release) passed before the commits. Closed DONE on 2026-06-13.