@@ -17,11 +17,12 @@ use std::collections::HashMap;
1717use std:: ffi:: { CStr , CString } ;
1818use std:: os:: raw:: { c_char, c_void} ;
1919use std:: str:: FromStr ;
20- use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
20+ use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
2121use std:: sync:: { Arc , Mutex } ;
2222use std:: time:: Duration ;
2323use tokio:: runtime:: Runtime ;
2424use tokio:: sync:: mpsc:: { error:: TryRecvError , UnboundedReceiver } ;
25+ use tokio_util:: sync:: CancellationToken ;
2526
2627/// Global callback registry for thread-safe callback management
2728static CALLBACK_REGISTRY : Lazy < Arc < Mutex < CallbackRegistry > > > =
@@ -104,12 +105,6 @@ struct SyncCallbackData {
104105 _marker : std:: marker:: PhantomData < ( ) > ,
105106}
106107
107- async fn wait_for_shutdown_signal ( signal : Arc < AtomicBool > ) {
108- while !signal. load ( Ordering :: Relaxed ) {
109- tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
110- }
111- }
112-
113108/// FFIDashSpvClient structure
114109type InnerClient = DashSpvClient <
115110 key_wallet_manager:: wallet_manager:: WalletManager <
@@ -126,7 +121,7 @@ pub struct FFIDashSpvClient {
126121 event_callbacks : Arc < Mutex < FFIEventCallbacks > > ,
127122 active_threads : Arc < Mutex < Vec < std:: thread:: JoinHandle < ( ) > > > > ,
128123 sync_callbacks : Arc < Mutex < Option < SyncCallbackData > > > ,
129- shutdown_signal : Arc < AtomicBool > ,
124+ shutdown_token : CancellationToken ,
130125 // Stored event receiver for pull-based draining (no background thread by default)
131126 event_rx : Arc < Mutex < Option < UnboundedReceiver < dash_spv:: types:: SpvEvent > > > > ,
132127}
@@ -197,7 +192,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_new(
197192 event_callbacks : Arc :: new ( Mutex :: new ( FFIEventCallbacks :: default ( ) ) ) ,
198193 active_threads : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
199194 sync_callbacks : Arc :: new ( Mutex :: new ( None ) ) ,
200- shutdown_signal : Arc :: new ( AtomicBool :: new ( false ) ) ,
195+ shutdown_token : CancellationToken :: new ( ) ,
201196 event_rx : Arc :: new ( Mutex :: new ( None ) ) ,
202197 } ;
203198 Box :: into_raw ( Box :: new ( ffi_client) )
@@ -378,8 +373,8 @@ pub unsafe extern "C" fn dash_spv_ffi_client_drain_events(client: *mut FFIDashSp
378373 FFIErrorCode :: Success as i32
379374}
380375
381- fn stop_client_internal ( client : & FFIDashSpvClient ) -> Result < ( ) , dash_spv:: SpvError > {
382- client. shutdown_signal . store ( true , Ordering :: Relaxed ) ;
376+ fn stop_client_internal ( client : & mut FFIDashSpvClient ) -> Result < ( ) , dash_spv:: SpvError > {
377+ client. shutdown_token . cancel ( ) ;
383378
384379 // Ensure callbacks are cleared so no further progress/completion notifications fire.
385380 {
@@ -411,7 +406,7 @@ fn stop_client_internal(client: &FFIDashSpvClient) -> Result<(), dash_spv::SpvEr
411406 res
412407 } ) ;
413408
414- client. shutdown_signal . store ( false , Ordering :: Relaxed ) ;
409+ client. shutdown_token = CancellationToken :: new ( ) ;
415410
416411 result
417412}
@@ -525,7 +520,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_start(client: *mut FFIDashSpvClient
525520pub unsafe extern "C" fn dash_spv_ffi_client_stop ( client : * mut FFIDashSpvClient ) -> i32 {
526521 null_check ! ( client) ;
527522
528- let client = & ( * client) ;
523+ let client = & mut ( * client) ;
529524 match stop_client_internal ( client) {
530525 Ok ( ( ) ) => FFIErrorCode :: Success as i32 ,
531526 Err ( e) => {
@@ -785,7 +780,6 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
785780 let inner = client. inner . clone ( ) ;
786781 let runtime = client. runtime . clone ( ) ;
787782 let sync_callbacks = client. sync_callbacks . clone ( ) ;
788- let shutdown_signal = client. shutdown_signal . clone ( ) ;
789783
790784 // Take progress receiver from client
791785 let progress_receiver = {
@@ -797,7 +791,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
797791 if let Some ( mut receiver) = progress_receiver {
798792 let runtime_handle = runtime. handle ( ) . clone ( ) ;
799793 let sync_callbacks_clone = sync_callbacks. clone ( ) ;
800- let shutdown_signal_clone = shutdown_signal . clone ( ) ;
794+ let shutdown_token_monitor = client . shutdown_token . clone ( ) ;
801795
802796 let handle = std:: thread:: spawn ( move || {
803797 runtime_handle. block_on ( async move {
@@ -859,7 +853,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
859853 None => break ,
860854 }
861855 }
862- _ = wait_for_shutdown_signal ( shutdown_signal_clone . clone ( ) ) => {
856+ _ = shutdown_token_monitor . cancelled ( ) => {
863857 break ;
864858 }
865859 }
@@ -874,15 +868,12 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
874868 // Spawn sync task in a separate thread with safe callback access
875869 let runtime_handle = runtime. handle ( ) . clone ( ) ;
876870 let sync_callbacks_clone = sync_callbacks. clone ( ) ;
877- let shutdown_signal_for_thread = shutdown_signal. clone ( ) ;
878- let stop_triggered_for_thread = Arc :: new ( AtomicBool :: new ( false ) ) ;
871+ let shutdown_token_sync = client. shutdown_token . clone ( ) ;
879872 let sync_handle = std:: thread:: spawn ( move || {
880- let stop_triggered_for_callback = stop_triggered_for_thread . clone ( ) ;
873+ let shutdown_token_callback = shutdown_token_sync . clone ( ) ;
881874 // Run monitoring loop
882875 let monitor_result = runtime_handle. block_on ( {
883876 let inner = inner. clone ( ) ;
884- let shutdown_signal_for_thread = shutdown_signal_for_thread. clone ( ) ;
885- let stop_triggered_for_thread = stop_triggered_for_callback. clone ( ) ;
886877 async move {
887878 let mut spv_client = {
888879 let mut guard = inner. lock ( ) . unwrap ( ) ;
@@ -903,8 +894,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
903894 Ok ( inner) => inner,
904895 Err ( _) => Ok ( ( ) ) ,
905896 } ,
906- _ = wait_for_shutdown_signal( shutdown_signal_for_thread. clone( ) ) => {
907- stop_triggered_for_thread. store( true , Ordering :: Relaxed ) ;
897+ _ = shutdown_token_sync. cancelled( ) => {
908898 abort_handle. abort( ) ;
909899 match monitor_future. as_mut( ) . await {
910900 Ok ( inner) => inner,
@@ -930,7 +920,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
930920 ..
931921 } ) = registry. unregister ( callback_data. callback_id )
932922 {
933- if stop_triggered_for_callback . load ( Ordering :: Relaxed ) {
923+ if shutdown_token_callback . is_cancelled ( ) {
934924 let msg = CString :: new ( "Sync stopped by request" ) . unwrap_or_else ( |_| {
935925 CString :: new ( "Sync stopped" ) . expect ( "hardcoded string is safe" )
936926 } ) ;
@@ -984,7 +974,7 @@ pub unsafe extern "C" fn dash_spv_ffi_client_sync_to_tip_with_progress(
984974pub unsafe extern "C" fn dash_spv_ffi_client_cancel_sync ( client : * mut FFIDashSpvClient ) -> i32 {
985975 null_check ! ( client) ;
986976
987- let client = & ( * client) ;
977+ let client = & mut ( * client) ;
988978
989979 match stop_client_internal ( client) {
990980 Ok ( ( ) ) => FFIErrorCode :: Success as i32 ,
@@ -1318,8 +1308,8 @@ pub unsafe extern "C" fn dash_spv_ffi_client_destroy(client: *mut FFIDashSpvClie
13181308 if !client. is_null ( ) {
13191309 let client = Box :: from_raw ( client) ;
13201310
1321- // Set shutdown signal to stop all threads
1322- client. shutdown_signal . store ( true , Ordering :: Relaxed ) ;
1311+ // Cancel shutdown token to stop all threads
1312+ client. shutdown_token . cancel ( ) ;
13231313
13241314 // Clean up any registered callbacks
13251315 if let Some ( ref callback_data) = * client. sync_callbacks . lock ( ) . unwrap ( ) {
0 commit comments