@@ -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