Skip to content

Commit fabbdad

Browse files
committed
fix: replace abort() with cooperative wait in wait_for_run_task
`abort()` can interrupt the cleanup sequence in `DashSpvClient::run()` (the `monitor_shutdown.cancel()` + `tokio::join!`), leaving monitor tasks running after FFI callback pointers are freed. Use cooperative wait with a timeout fallback instead.
1 parent aaddece commit fabbdad

1 file changed

Lines changed: 21 additions & 9 deletions

File tree

dash-spv-ffi/src/client.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,23 +110,35 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new(
110110
}
111111
}
112112

113+
/// Maximum time to wait for the run task to exit cooperatively before aborting.
114+
const RUN_TASK_SHUTDOWN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
115+
113116
impl FFIDashSpvClient {
114-
/// Cancel the run task and wait for it to finish.
115-
fn cancel_run_task(&self) {
117+
/// Wait for the run task to finish cooperatively, aborting only on timeout.
118+
///
119+
/// The caller must cancel `shutdown_token` before calling this so that
120+
/// `DashSpvClient::run()` exits its loop and cleans up monitor tasks.
121+
/// Only falls back to `abort()` if the task doesn't exit within the timeout.
122+
fn wait_for_run_task(&self) {
116123
let task = self.run_task.lock().unwrap().take();
117124
if let Some(task) = task {
118-
task.abort();
119-
self.runtime.block_on(async {
120-
let _ = task.await;
121-
});
125+
let finished = self
126+
.runtime
127+
.block_on(async { tokio::time::timeout(RUN_TASK_SHUTDOWN_TIMEOUT, task).await });
128+
if finished.is_err() {
129+
tracing::warn!(
130+
"Run task did not exit within {:?}, aborting",
131+
RUN_TASK_SHUTDOWN_TIMEOUT
132+
);
133+
}
122134
}
123135
}
124136
}
125137

126138
fn stop_client_internal(client: &mut FFIDashSpvClient) -> Result<(), dash_spv::SpvError> {
127139
client.shutdown_token.cancel();
128140

129-
client.cancel_run_task();
141+
client.wait_for_run_task();
130142

131143
let result = client.runtime.block_on(async { client.inner.stop().await });
132144

@@ -342,8 +354,8 @@ pub unsafe extern "C" fn dash_spv_ffi_client_destroy(client: *mut FFIDashSpvClie
342354
let _ = client.inner.stop().await;
343355
});
344356

345-
// Abort and await the run task
346-
client.cancel_run_task();
357+
// Wait for the run task to finish (cooperative, with timeout fallback)
358+
client.wait_for_run_task();
347359

348360
tracing::info!("FFI client destroyed and all tasks cleaned up");
349361
}

0 commit comments

Comments
 (0)