Skip to content

perf(files): move blocking files.rs commands off the GTK main thread#89

Merged
Zeus-Deus merged 2 commits into
mainfrom
feature/79-perf-move-blocking-files-rs-commands-off-the-gtk-main
Jun 10, 2026
Merged

perf(files): move blocking files.rs commands off the GTK main thread#89
Zeus-Deus merged 2 commits into
mainfrom
feature/79-perf-move-blocking-files-rs-commands-off-the-gtk-main

Conversation

@Zeus-Deus

Copy link
Copy Markdown
Owner

Summary

Eight files.rs commands were sync #[tauri::command] fns that either spawn subprocesses (git check-ignore, rg/grep, fd/find, which+xdg-open) or do blocking file I/O. In Tauri v2 a sync command runs on the GTK main thread, so a slow disk or wedged subprocess froze the whole UI — the subprocess-spawning ones can hang indefinitely.

This converts all of them to async fn + tokio::task::spawn_blocking, the same pattern already applied in commands/git.rs (see its header note) and commands/workspace.rs (#85). Frontend invoke() already returns a Promise, so no caller changes are needed.

Converted commands:

  • Subprocess spawners (worst case): list_directory, search_in_files, search_file_names, reveal_in_file_manager
  • Blocking I/O: read_file, write_file, save_clipboard_image_bytes, paste_clipboard_image_to_file — the latter also moves the OS clipboard round-trip and the CPU-heavy PNG encode (~8 MB RGBA for a 1080p screenshot) onto the blocking pool

No State guards exist in these commands, so nothing non-Send is held across an .await.

Testing

  • Functional tests through real subprocesses (13 new): temp git repo driven through real git check-ignore (gitignore flagging), real rg/grep (match offsets, line numbers), real fd/find (relative paths), plus read/write round-trip and binary/oversize/missing rejections. Gated for Windows CI where needed (no fd/POSIX find there).
  • Executor-thread regression test: write_file against a FIFO with no reader on a current_thread runtime — a timer sharing the executor thread must fire while the write is wedged in open(2). A rescuer thread un-wedges a regressed (inline-blocking) build so the test fails fast instead of hanging. Validated by mutation: reverting write_file to inline blocking makes the test fail with the exact diagnostic.
  • Real IPC dispatch e2e (tests/files_commands_ipc.rs): drives the converted commands through the actual generate_handler! pipeline via tauri::test::get_ipc_response, locking the camelCase wire args (showHidden, maxResults), async dispatch, result serialization, and the error channel.
  • Real app: booted npm run tauri:dev (real GTK main thread, real IPC) — clean startup, control socket responsive, no panics.
  • cargo check, all 24 integration test targets, npm run check, and 1826/1826 vitest tests pass. The one local lib-test failure (mcp::project_codemux_entry_is_filtered_out) reproduces identically on the pristine baseline — it's host-config pollution (a user-scope codemux MCP server entry), unrelated to this change and green on CI's clean HOME.

Closes #79

Zeus-Deus added 2 commits June 9, 2026 23:49
Eight files.rs commands were sync #[tauri::command] fns that either
spawn subprocesses (git check-ignore, rg/grep, fd/find, which+xdg-open)
or do blocking file I/O. In Tauri v2 a sync command runs on the GTK
main thread, so a slow disk or wedged subprocess froze the whole UI —
the subprocess-spawning ones can hang indefinitely.

- Convert list_directory, search_in_files, search_file_names,
  reveal_in_file_manager, read_file, write_file,
  save_clipboard_image_bytes, and paste_clipboard_image_to_file to
  async fn wrapping the blocking work in tokio::task::spawn_blocking.
- paste_clipboard_image_to_file also moves the clipboard read (an OS
  round-trip) and the CPU-heavy PNG encode onto the blocking pool.
- Add functional tests driving the real subprocess paths (git
  check-ignore gitignore flagging, rg/grep matches + offsets, fd/find
  relative paths) plus read/write round-trip and rejection paths.
- Add a FIFO-based regression test on a current_thread runtime proving
  a wedged blocking write no longer stalls the executor thread (a
  reader thread un-wedges a regressed build so the test fails fast
  instead of hanging); validated by mutating write_file back to inline
  blocking and watching it fail.
- Add tests/files_commands_ipc.rs: drives the converted commands
  through the real generate_handler!/IPC dispatch via tauri::test,
  locking the camelCase wire args (showHidden, maxResults) and the
  error channel.

Same pattern as commands/git.rs (see its header note). Frontend
invoke() already returns a Promise, so no caller changes are needed.

Closes #79
The Windows CI leg failed list_directory_marks_gitignored_entries:
git_ignored_set fed ABSOLUTE paths to `git check-ignore --stdin`, and
git C-quotes any echoed output line containing "unusual" bytes — the
backslashes of every Windows absolute path, or non-ASCII bytes via
core.quotepath on all platforms. The quoted line ("C:\\Users\\...")
yields a basename with a stray trailing quote that never matches the
entry name, so ignored entries silently lost their flag. Confirmed via
a temporary diagnostic run on the Windows runner: check-ignore matched
the absolute path fine (exit 0) but returned it C-quoted.

Fix: feed cwd-relative entry names and switch to `-z` (NUL-separated
input/output, quoting disabled) so the echoed bytes match the fed
names exactly on every platform, regardless of how the caller spelled
the directory path.

Tests:
- list_directory_marks_gitignored_entries_with_non_ascii_names
  reproduces the Windows failure class on Linux (fails on the old
  parser, passes now).
- list_directory_marks_gitignored_entries_via_symlinked_path locks in
  flagging through symlinked paths (macOS /tmp → /private/tmp shape).
@Zeus-Deus Zeus-Deus force-pushed the feature/79-perf-move-blocking-files-rs-commands-off-the-gtk-main branch from 9546d60 to 722431c Compare June 9, 2026 22:26
@Zeus-Deus Zeus-Deus merged commit c494ba7 into main Jun 10, 2026
4 checks passed
@Zeus-Deus Zeus-Deus deleted the feature/79-perf-move-blocking-files-rs-commands-off-the-gtk-main branch June 10, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[perf] Move blocking files.rs commands off the GTK main thread (async + spawn_blocking)

1 participant