tui: async storage operations — don't block event loop on gallery/entity loads #125

Closed
opened 2026-04-12 22:03:06 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As a user with a large gallery (1000+ items), I want the TUI to stay responsive while loading data, so that navigation and input aren't frozen during storage reads.

Problem

Several screens perform synchronous SQLite reads inside handle() or render():

  • gallery.rs load_from_storage() — calls guard.list(&filter) synchronously
  • entities.rs load_from_storage() — calls guard.list_entities() synchronously
  • presets.rs load_from_storage() — calls guard.list_presets() synchronously

These hold a std::sync::Mutex lock on GalleryStorage and block the tokio event loop. For small datasets this is fine, but with large galleries the UI freezes.

Acceptance criteria

  • Gallery load_from_storage() spawns a tokio::spawn_blocking task instead of blocking inline
  • Results are routed back via AppAction::GalleryLoaded(Vec<GalleryItem>) or similar
  • A loading indicator is shown while data is in flight ("Loading..." placeholder)
  • Entity and Preset screens follow the same async pattern
  • Multiple rapid loads are debounced (only the latest result is applied)
  • Storage mutex is held only for the duration of the query, not across await points

Out of scope

  • Pagination / virtual scrolling (separate optimization)
  • Background indexing

References

  • crates/loom-tui/src/screens/gallery.rsload_from_storage()
  • crates/loom-tui/src/screens/entities.rsload_from_storage()
  • crates/loom-tui/src/screens/presets.rsload_from_storage()
  • tokio::task::spawn_blocking for CPU-bound / mutex-holding work
## User story As a user with a large gallery (1000+ items), I want the TUI to stay responsive while loading data, so that navigation and input aren't frozen during storage reads. ## Problem Several screens perform synchronous SQLite reads inside `handle()` or `render()`: - `gallery.rs` `load_from_storage()` — calls `guard.list(&filter)` synchronously - `entities.rs` `load_from_storage()` — calls `guard.list_entities()` synchronously - `presets.rs` `load_from_storage()` — calls `guard.list_presets()` synchronously These hold a `std::sync::Mutex` lock on `GalleryStorage` and block the tokio event loop. For small datasets this is fine, but with large galleries the UI freezes. ## Acceptance criteria - [ ] Gallery `load_from_storage()` spawns a `tokio::spawn_blocking` task instead of blocking inline - [ ] Results are routed back via `AppAction::GalleryLoaded(Vec<GalleryItem>)` or similar - [ ] A loading indicator is shown while data is in flight ("Loading..." placeholder) - [ ] Entity and Preset screens follow the same async pattern - [ ] Multiple rapid loads are debounced (only the latest result is applied) - [ ] Storage mutex is held only for the duration of the query, not across await points ## Out of scope - Pagination / virtual scrolling (separate optimization) - Background indexing ## References - `crates/loom-tui/src/screens/gallery.rs` — `load_from_storage()` - `crates/loom-tui/src/screens/entities.rs` — `load_from_storage()` - `crates/loom-tui/src/screens/presets.rs` — `load_from_storage()` - `tokio::task::spawn_blocking` for CPU-bound / mutex-holding work
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
charles/loom#125
No description provided.