-
Notifications
You must be signed in to change notification settings - Fork 56
fix(platform-wallet): free SPV data dir on stop #3811
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| //! SPV client runtime — manages the DashSpvClient lifecycle. | ||
|
|
||
| use std::sync::Arc; | ||
| use std::sync::{Arc, Mutex}; | ||
|
|
||
| use tokio::sync::RwLock; | ||
| use tokio::task::JoinHandle; | ||
|
|
||
| use dashcore::sml::llmq_type::LLMQType; | ||
| use dashcore::{QuorumHash, Transaction}; | ||
|
|
@@ -29,6 +30,7 @@ pub struct SpvRuntime { | |
| event_manager: Arc<PlatformEventManager>, | ||
| wallet_manager: Arc<RwLock<WalletManager<PlatformWalletInfo>>>, | ||
| client: RwLock<Option<SpvClient>>, | ||
| task: Mutex<Option<JoinHandle<()>>>, | ||
| } | ||
| // TODO: We want it better | ||
| impl SpvRuntime { | ||
|
|
@@ -41,6 +43,7 @@ impl SpvRuntime { | |
| event_manager, | ||
| wallet_manager, | ||
| client: RwLock::new(None), | ||
| task: Mutex::new(None), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -153,27 +156,62 @@ impl SpvRuntime { | |
| result | ||
| } | ||
|
|
||
| /// Stop SPV sync gracefully. | ||
| /// Stop SPV sync gracefully. Unlocks the data dir safely | ||
| pub async fn stop(&self) -> Result<(), PlatformWalletError> { | ||
| let mut client = self.client.write().await; | ||
| if let Some(c) = client.take() { | ||
| c.stop() | ||
| let taken = { | ||
| let mut client = self.client.write().await; | ||
| client.take() | ||
| }; | ||
|
|
||
| let stop_result = match taken { | ||
| Some(c) => c | ||
| .stop() | ||
| .await | ||
| .map_err(|e| PlatformWalletError::SpvError(e.to_string())), | ||
| None => Ok(()), | ||
| }; | ||
|
|
||
| let handle = self.task.lock().expect("spv task mutex poisoned").take(); | ||
| if let Some(handle) = handle { | ||
| let abort = handle.abort_handle(); | ||
| if tokio::time::timeout(std::time::Duration::from_secs(15), handle) | ||
| .await | ||
| .map_err(|e| PlatformWalletError::SpvError(e.to_string()))?; | ||
| .is_err() | ||
| { | ||
| tracing::warn!( | ||
| "SPV stop: background run loop did not unwind within 15s; aborting it" | ||
| ); | ||
|
|
||
| abort.abort(); | ||
| } | ||
| } | ||
| Ok(()) | ||
|
|
||
| stop_result | ||
|
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
Comment on lines
+174
to
+189
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: stop() returns Ok even after forced cancellation, hiding incomplete shutdown stop_result is computed at lines 166-172 from the snapshot taken before the JoinHandle await. When the 15s timeout fires at line 177 and Because this PR is specifically about 'free SPV data dir on stop', the return value should distinguish a clean stop from a forced abort. Either introduce a source: ['claude', 'codex'] |
||
| } | ||
|
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
|
||
|
|
||
| /// Spawn `run()` on the current tokio runtime and return immediately. | ||
| /// | ||
| /// Call [`stop`] to stop it | ||
| pub fn spawn_in_background(self: &Arc<Self>, config: ClientConfig) { | ||
| { | ||
| let existing = self.task.lock().expect("spv task mutex poisoned"); | ||
| if existing.is_some() { | ||
|
ZocoLini marked this conversation as resolved.
|
||
| tracing::warn!( | ||
| "spawn_in_background called while a task is already running; ignoring" | ||
| ); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| let this = Arc::clone(self); | ||
| tokio::spawn(async move { | ||
|
|
||
| let handle = tokio::spawn(async move { | ||
| if let Err(e) = this.run(config).await { | ||
| tracing::warn!("SpvRuntime background run exited with error: {}", e); | ||
| } | ||
| }); | ||
|
|
||
| *self.task.lock().expect("spv task mutex poisoned") = Some(handle); | ||
| } | ||
|
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
ZocoLini marked this conversation as resolved.
Comment on lines
195
to
215
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Blocking: spawn_in_background guard is non-atomic and never clears finished JoinHandles The duplicate-task guard is split across two separate std::sync::Mutex acquisitions: the inner scope at lines 197-204 checks The same guard also treats any Fix shape: hold a single mutex (or a unified lifecycle state) across the check, the spawn, and the store; and before treating source: ['claude', 'codex'] |
||
|
|
||
| /// Get the current sync progress. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.