@@ -31,7 +31,7 @@ use lightning_block_sync::{
3131} ;
3232use serde:: Serialize ;
3333
34- use super :: WalletSyncStatus ;
34+ use super :: { WalletSyncGuard , WalletSyncStatus } ;
3535use crate :: config:: {
3636 BitcoindRestClientConfig , Config , DEFAULT_FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS ,
3737 DEFAULT_TX_BROADCAST_TIMEOUT_SECS ,
@@ -52,6 +52,31 @@ const CHAIN_POLLING_TIMEOUT_SECS: u64 = 10;
5252type BitcoindSpvClient =
5353 SpvClient < ChainPoller < Arc < BitcoindClient > , BitcoindClient > , Arc < ChainListener > > ;
5454
55+ async fn acquire_initial_wallet_sync_guard < ' a > (
56+ wallet_polling_status : & ' a Mutex < WalletSyncStatus > ,
57+ stop_sync_receiver : & mut tokio:: sync:: watch:: Receiver < ( ) > ,
58+ ) -> Option < WalletSyncGuard < ' a > > {
59+ loop {
60+ let mut pending_sync = {
61+ let mut status_lock = wallet_polling_status. lock ( ) . expect ( "lock" ) ;
62+ match status_lock. register_or_subscribe_pending_sync ( ) {
63+ Some ( pending_sync) => pending_sync,
64+ None => {
65+ return Some ( WalletSyncGuard :: new (
66+ wallet_polling_status,
67+ Error :: WalletOperationFailed ,
68+ ) ) ;
69+ } ,
70+ }
71+ } ;
72+ tokio:: select! {
73+ biased;
74+ _ = stop_sync_receiver. changed( ) => return None ,
75+ _ = pending_sync. recv( ) => { } ,
76+ }
77+ }
78+ }
79+
5580pub ( super ) struct BitcoindChainSource {
5681 api_client : Arc < BitcoindClient > ,
5782 spv_client : tokio:: sync:: Mutex < Option < BitcoindSpvClient > > ,
@@ -160,12 +185,13 @@ impl BitcoindChainSource {
160185 ) {
161186 // First register for the wallet polling status to make sure `Node::sync_wallets` calls
162187 // wait on the result before proceeding.
163- {
164- let mut status_lock = self . wallet_polling_status . lock ( ) . expect ( "lock" ) ;
165- if status_lock. register_or_subscribe_pending_sync ( ) . is_some ( ) {
166- debug_assert ! ( false , "Sync already in progress. This should never happen." ) ;
167- }
168- }
188+ let Some ( initial_sync_guard) =
189+ acquire_initial_wallet_sync_guard ( & self . wallet_polling_status , & mut stop_sync_receiver)
190+ . await
191+ else {
192+ log_trace ! ( self . logger, "Stopping initial chain sync." ) ;
193+ return ;
194+ } ;
169195
170196 log_info ! (
171197 self . logger,
@@ -302,7 +328,7 @@ impl BitcoindChainSource {
302328 }
303329
304330 // Now propagate the initial result to unblock waiting subscribers.
305- self . wallet_polling_status . lock ( ) . expect ( "lock" ) . propagate_result_to_subscribers ( Ok ( ( ) ) ) ;
331+ initial_sync_guard . complete ( Ok ( ( ) ) ) ;
306332
307333 let mut chain_polling_interval =
308334 tokio:: time:: interval ( Duration :: from_secs ( CHAIN_POLLING_INTERVAL_SECS ) ) ;
@@ -413,6 +439,8 @@ impl BitcoindChainSource {
413439 Error :: WalletOperationFailed
414440 } ) ?;
415441 }
442+ let sync_guard =
443+ WalletSyncGuard :: new ( & self . wallet_polling_status , Error :: WalletOperationFailed ) ;
416444
417445 let res = self
418446 . poll_and_update_listeners_inner (
@@ -423,7 +451,7 @@ impl BitcoindChainSource {
423451 )
424452 . await ;
425453
426- self . wallet_polling_status . lock ( ) . expect ( "lock" ) . propagate_result_to_subscribers ( res) ;
454+ sync_guard . complete ( res) ;
427455
428456 res
429457 }
@@ -1588,6 +1616,9 @@ impl std::error::Error for BitcoindClientError {}
15881616
15891617#[ cfg( test) ]
15901618mod tests {
1619+ use std:: sync:: Mutex ;
1620+ use std:: time:: Duration ;
1621+
15911622 use bitcoin:: hashes:: Hash ;
15921623 use bitcoin:: { FeeRate , OutPoint , ScriptBuf , Transaction , TxIn , TxOut , Txid , Witness } ;
15931624 use lightning_block_sync:: http:: JsonResponse ;
@@ -1597,9 +1628,36 @@ mod tests {
15971628 use serde_json:: json;
15981629
15991630 use crate :: chain:: bitcoind:: {
1600- FeeResponse , GetMempoolEntryResponse , GetRawMempoolResponse , GetRawTransactionResponse ,
1601- MempoolMinFeeResponse ,
1631+ acquire_initial_wallet_sync_guard , FeeResponse , GetMempoolEntryResponse ,
1632+ GetRawMempoolResponse , GetRawTransactionResponse , MempoolMinFeeResponse ,
16021633 } ;
1634+ use crate :: chain:: { WalletSyncGuard , WalletSyncStatus } ;
1635+ use crate :: Error ;
1636+
1637+ #[ tokio:: test]
1638+ async fn initial_sync_waits_for_in_progress_sync ( ) {
1639+ let status = Mutex :: new ( WalletSyncStatus :: Completed ) ;
1640+ assert ! ( status. lock( ) . expect( "lock" ) . register_or_subscribe_pending_sync( ) . is_none( ) ) ;
1641+ let in_progress_guard = WalletSyncGuard :: new ( & status, Error :: WalletOperationFailed ) ;
1642+ let ( _stop_sender, mut stop_receiver) = tokio:: sync:: watch:: channel ( ( ) ) ;
1643+ let mut acquire_guard =
1644+ Box :: pin ( acquire_initial_wallet_sync_guard ( & status, & mut stop_receiver) ) ;
1645+
1646+ let early_result =
1647+ tokio:: time:: timeout ( Duration :: from_millis ( 10 ) , acquire_guard. as_mut ( ) ) . await ;
1648+ assert ! ( early_result. is_err( ) , "background sync should wait for the active sync" ) ;
1649+
1650+ in_progress_guard. complete ( Ok ( ( ) ) ) ;
1651+ let acquired_guard = tokio:: time:: timeout ( Duration :: from_secs ( 1 ) , acquire_guard)
1652+ . await
1653+ . expect ( "background sync should resume" )
1654+ . expect ( "background sync should acquire the sync guard" ) ;
1655+ assert ! (
1656+ matches!( * status. lock( ) . expect( "lock" ) , WalletSyncStatus :: InProgress { .. } ) ,
1657+ "background sync should own the next sync"
1658+ ) ;
1659+ acquired_guard. complete ( Ok ( ( ) ) ) ;
1660+ }
16031661
16041662 prop_compose ! {
16051663 fn arbitrary_witness( ) (
0 commit comments