Skip to content

fix(cli): interrupt sync before draining the save task on quit - #2746

Open
zancas wants to merge 2 commits into
devfrom
save_hang
Open

fix(cli): interrupt sync before draining the save task on quit#2746
zancas wants to merge 2 commits into
devfrom
save_hang

Conversation

@zancas

@zancas zancas commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes ZIN-70: https://linear.app/zingo-mobile/issue/ZIN-70/quit-hangs-during-sync-the-save-task-shutdown-waits-behind-the-scan

Problem

quit drained only the save task. The save loop observes its shutdown flag only after it wins the wallet write lock and completes a full save. During a sync the scan holds that lock for long stretches. Nothing told the sync task to stop. On a fresh wallet's initial sync the session spun on quit: working until the user killed it.

Fix

quit now stops the sync task first and awaits the engine's answer. Only then does it drain the save task. The save loop's next cycle wins the lock promptly and the shutdown completes.

Verification

cargo check, clippy, and fmt are clean. A new acceptance test, zingo-cli/tests/quit_mid_sync.rs, drives a real session against a mainnet indexer. It issues quit after the sync span opens. It asserts the process exits within a bounded time and leaves a wallet file a fresh session loads. The test is network-bound and ignored by default.

🤖 Generated with Claude Code

zancas and others added 2 commits August 26, 2026 09:51
A quit issued while a sync was in flight waited behind the scan: the save
loop observes its shutdown flag only after it wins the wallet write lock
and completes a full save, and nothing told the sync task to stop. On a
fresh wallet's initial sync the session spun on "quit: working" until it
was killed. This serves ZIN-70.

quit now stops the sync task first and awaits the engine's answer, and only
then drains the save task, whose next cycle wins the lock promptly. A
network-bound acceptance test, ignored by default, drives a real session
against a mainnet indexer, issues quit mid-sync, and asserts the process
exits within a bounded time and leaves a wallet file a fresh session loads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The review of the quit-interrupts-sync fix (PR #2746, ZIN-70) confirmed six
findings, and this change resolves all of them, with each behavioral fix
proven by a test that failed before it.

zingolib gains LightClient::shutdown_sync, a bounded graceful stop that
reads the task through poll_sync and aborts the engine when it outlives
the bound, reporting the outcome as the new SyncShutdown enum. Unlike
await_sync, the interrupted path never captures part witnesses over
half-synced state, and the typed outcome ends the mislabeled "sync stop
failed" report. A falsifier with a fabricated wedged sync task pins the
bound.

quit now drives shutdown_sync, and the interactive session's Ctrl-C,
Ctrl-D, and terminal-error arms dispatch the same quit teardown before
breaking, so no exit path abandons the sync task or skips the save drain.
A new offline acceptance test ends a session by closing stdin and asserts
the teardown's trailer.

The mid-sync acceptance test now holds quit until pepper-sync reports a
scanned batch processed, so it cannot pass by quitting during pre-scan
initialization; its session inherits stdout and stderr so failures carry
the session's own words; and the constants and proxy resolution it shared
verbatim with sync_bench_cli.rs now live once in a shared support module.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant