Skip to content

Commit 5eb9bbd

Browse files
Merge pull request #173 from dashpay/fix/ffi-event-throttling-and-memory-leak
fix: prevent FFI event flooding and memory leak in progress callbacks
2 parents 7f74751 + e10ef0f commit 5eb9bbd

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

dash-spv-ffi/src/client.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,14 @@ impl FFIDashSpvClient {
239239
return;
240240
};
241241
let callbacks = self.event_callbacks.lock().unwrap();
242+
// Prevent flooding the UI/main thread by limiting events per drain call.
243+
// Remaining events stay queued and will be drained on the next tick.
244+
let max_events_per_call: usize = 500;
245+
let mut processed: usize = 0;
242246
loop {
247+
if processed >= max_events_per_call {
248+
break;
249+
}
243250
match rx.try_recv() {
244251
Ok(event) => match event {
245252
dash_spv::types::SpvEvent::BalanceUpdate {
@@ -354,6 +361,7 @@ impl FFIDashSpvClient {
354361
break;
355362
}
356363
}
364+
processed += 1;
357365
}
358366
}
359367
}
@@ -804,8 +812,8 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
804812
SyncStage::Complete | SyncStage::Failed(_)
805813
);
806814

807-
// Create FFI progress
808-
let ffi_progress = Box::new(FFIDetailedSyncProgress::from(progress));
815+
// Create FFI progress (stack-allocated to avoid double-free issues)
816+
let mut ffi_progress = FFIDetailedSyncProgress::from(progress);
809817

810818
// Call the callback using the registry
811819
{
@@ -822,7 +830,24 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
822830
// SAFETY: The callback and user_data are safely stored in the registry
823831
// and accessed through thread-safe mechanisms. The registry ensures
824832
// proper lifetime management without raw pointer passing across threads.
825-
callback(ffi_progress.as_ref(), *user_data);
833+
callback(&ffi_progress, *user_data);
834+
835+
// Free any heap-allocated strings inside the progress struct
836+
// to avoid leaking per-callback allocations (e.g., stage_message).
837+
// Move stage_message out of the struct to avoid double-free.
838+
unsafe {
839+
// Move stage_message out of the struct (not using ptr::read to avoid double-free)
840+
let stage_message = std::mem::replace(
841+
&mut ffi_progress.stage_message,
842+
crate::types::FFIString {
843+
ptr: std::ptr::null_mut(),
844+
length: 0,
845+
},
846+
);
847+
// Destroy stage_message allocated in FFIDetailedSyncProgress::from
848+
crate::types::dash_spv_ffi_string_destroy(stage_message);
849+
// ffi_progress will be dropped normally here; no Drop impl exists
850+
}
826851
}
827852
}
828853
}

0 commit comments

Comments
 (0)