diff --git a/dash-spv-ffi/src/client.rs b/dash-spv-ffi/src/client.rs index f45fb1ee5..e39fd9c91 100644 --- a/dash-spv-ffi/src/client.rs +++ b/dash-spv-ffi/src/client.rs @@ -239,7 +239,14 @@ impl FFIDashSpvClient { return; }; let callbacks = self.event_callbacks.lock().unwrap(); + // Prevent flooding the UI/main thread by limiting events per drain call. + // Remaining events stay queued and will be drained on the next tick. + let max_events_per_call: usize = 500; + let mut processed: usize = 0; loop { + if processed >= max_events_per_call { + break; + } match rx.try_recv() { Ok(event) => match event { dash_spv::types::SpvEvent::BalanceUpdate { @@ -354,6 +361,7 @@ impl FFIDashSpvClient { break; } } + processed += 1; } } } @@ -804,8 +812,8 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress( SyncStage::Complete | SyncStage::Failed(_) ); - // Create FFI progress - let ffi_progress = Box::new(FFIDetailedSyncProgress::from(progress)); + // Create FFI progress (stack-allocated to avoid double-free issues) + let mut ffi_progress = FFIDetailedSyncProgress::from(progress); // Call the callback using the registry { @@ -822,7 +830,24 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress( // SAFETY: The callback and user_data are safely stored in the registry // and accessed through thread-safe mechanisms. The registry ensures // proper lifetime management without raw pointer passing across threads. - callback(ffi_progress.as_ref(), *user_data); + callback(&ffi_progress, *user_data); + + // Free any heap-allocated strings inside the progress struct + // to avoid leaking per-callback allocations (e.g., stage_message). + // Move stage_message out of the struct to avoid double-free. + unsafe { + // Move stage_message out of the struct (not using ptr::read to avoid double-free) + let stage_message = std::mem::replace( + &mut ffi_progress.stage_message, + crate::types::FFIString { + ptr: std::ptr::null_mut(), + length: 0, + }, + ); + // Destroy stage_message allocated in FFIDetailedSyncProgress::from + crate::types::dash_spv_ffi_string_destroy(stage_message); + // ffi_progress will be dropped normally here; no Drop impl exists + } } } }