Skip to content

Shielded: ShieldedSyncManager::stop() is cancel-only, not a quiescence barrier (Clear-flow race) #3706

@QuantumExplorer

Description

@QuantumExplorer

What

ShieldedSyncManager::stop() (packages/rs-platform-wallet/src/manager/shielded_sync.rs) takes the cancellation token and calls cancel(). That prevents future loop iterations from starting, but does not join the background thread or await an in-flight coordinator.sync() pass.

Why it needs fixing

The method name + doc read like a synchronization barrier, but it's best-effort cancellation. A pass that already snapshotted the subwallet list can run to completion after stop() returns and reach the persister fan-out, queuing changesets.

This bites the Clear flow: platform_wallet_manager_shielded_clear's doc says "stop the loop first so the next pass can't race the registry clear", but because stop() doesn't actually wait, an in-flight pass can re-create PersistentShieldedNote / PersistentShieldedSyncState rows after the host's SwiftData wipe — the exact symptom Clear is meant to prevent. (Narrowed window, not guaranteed, hence suggestion-level.)

Fix

Either give stop() real quiescence (await the running pass before returning) and keep the Clear contract honest, or rename it to cancel() and add a separate quiesce() that the Clear flow calls. Needs care around the dedicated OS thread the sync loop runs on and the is_syncing flag.

Deferred from #3603 review (thepastaclaw).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions