fix(lock): clear in-flight unlock/PIN/encrypt flags on disconnect so the lock screen can't wedge

Concurrency audit: RPCWorker::stop() discards any MainCb the fast worker pushed after the last drain,
and lock_unlock_in_progress_ / pin_in_progress_ / encrypt_in_progress_ are cleared only inside those
callbacks. So a disconnect (3-failure threshold, daemon restart) mid-unlock destroyed the callback that
would have cleared the flag, leaving the lock screen stuck on "Unlocking..." until the app was restarted.

Clear the three flags in onDisconnected() right after the fast worker is torn down (gated on it having
existed, since only fast-lane-posted callbacks can be lost this way). Chosen over draining the worker,
which would run stale callbacks after state_/rpc_session_ were already reset. The user can retry after
reconnect and the real encryption state is re-read on connect. No fund-path call is touched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 14:39:08 -05:00
co-authored by Claude Opus 4.8
parent 165675b131
commit 6d58802127
+7
View File
@@ -952,6 +952,13 @@ void App::onDisconnected(const std::string& reason)
// the fast lane dead for the rest of the session — which silently stalls the opid poll
// (its post() never runs, so a completed send spins on "Waiting for operation").
fast_worker_.reset();
// stop() discards undrained MainCbs, and these in-progress flags are cleared only inside
// those callbacks (unlock/lock/PIN/encrypt are posted on the fast lane when it is running).
// Release them here so the lock screen cannot show "Unlocking..." forever; the user can retry
// after reconnect, and the real encryption state is re-read on connect.
lock_unlock_in_progress_ = false;
pin_in_progress_ = false;
encrypt_in_progress_ = false;
}
if (fast_rpc_) {
fast_rpc_->disconnect();