Skip to content

Commit a1651fc

Browse files
authored
Merge pull request #736 from TheBlueMatt/2025-12-circular-arc-refs
Fix two circular `Arc` references
2 parents 5ea8f3c + 6a32f36 commit a1651fc

File tree

8 files changed

+79
-62
lines changed

8 files changed

+79
-62
lines changed

.github/workflows/rust.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ jobs:
8080
- name: Test on Rust ${{ matrix.toolchain }}
8181
if: "matrix.platform != 'windows-latest'"
8282
run: |
83-
RUSTFLAGS="--cfg no_download" cargo test
83+
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test
8484
- name: Test with UniFFI support on Rust ${{ matrix.toolchain }}
8585
if: "matrix.platform != 'windows-latest' && matrix.build-uniffi"
8686
run: |
87-
RUSTFLAGS="--cfg no_download" cargo test --features uniffi
87+
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test --features uniffi
8888
8989
doc:
9090
name: Documentation

.github/workflows/vss-integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@ jobs:
4545
cd ldk-node
4646
export TEST_VSS_BASE_URL="http://localhost:8080/vss"
4747
RUSTFLAGS="--cfg vss_test" cargo test io::vss_store
48-
RUSTFLAGS="--cfg vss_test" cargo test --test integration_tests_vss
48+
RUSTFLAGS="--cfg vss_test --cfg cycle_tests" cargo test --test integration_tests_vss

Cargo.toml

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ default = []
3939
#lightning-liquidity = { version = "0.2.0", features = ["std"] }
4040
#lightning-macros = { version = "0.2.0" }
4141

42-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["std"] }
43-
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0" }
44-
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["std"] }
45-
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0" }
46-
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["tokio"] }
47-
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0" }
48-
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0" }
49-
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["rest-client", "rpc-client", "tokio"] }
50-
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51-
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["std"] }
52-
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0" }
42+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["std"] }
43+
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
44+
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["std"] }
45+
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
46+
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["tokio"] }
47+
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
48+
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
49+
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["rest-client", "rpc-client", "tokio"] }
50+
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["esplora-async-https", "time", "electrum-rustls-ring"] }
51+
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["std"] }
52+
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891" }
5353

5454
bdk_chain = { version = "0.23.0", default-features = false, features = ["std"] }
5555
bdk_esplora = { version = "0.22.0", default-features = false, features = ["async-https-rustls", "tokio"]}
@@ -78,13 +78,13 @@ log = { version = "0.4.22", default-features = false, features = ["std"]}
7878
vss-client = { package = "vss-client-ng", version = "0.4" }
7979
prost = { version = "0.11.6", default-features = false}
8080
#bitcoin-payment-instructions = { version = "0.6" }
81-
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "a9ad849a0eb7b155a688d713de6d9010cb48f073" }
81+
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "fdca6c62f2fe2c53427d3e51e322a49aa7323ee2" }
8282

8383
[target.'cfg(windows)'.dependencies]
8484
winapi = { version = "0.3", features = ["winbase"] }
8585

8686
[dev-dependencies]
87-
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "1c730c8a16e28cc8e0c4817717ee63c97abcf4b0", features = ["std", "_test_utils"] }
87+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "5236dba053a3f4f01cf0c32ce42b609a93738891", features = ["std", "_test_utils"] }
8888
proptest = "1.0.0"
8989
regex = "1.5.6"
9090
criterion = { version = "0.7.0", features = ["async_tokio"] }
@@ -122,6 +122,7 @@ check-cfg = [
122122
"cfg(tokio_unstable)",
123123
"cfg(cln_test)",
124124
"cfg(lnd_test)",
125+
"cfg(cycle_tests)",
125126
]
126127

127128
[[bench]]

src/builder.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,8 +1481,12 @@ fn build_with_store_internal(
14811481

14821482
let gossip_source = match gossip_source_config {
14831483
GossipSourceConfig::P2PNetwork => {
1484-
let p2p_source =
1485-
Arc::new(GossipSource::new_p2p(Arc::clone(&network_graph), Arc::clone(&logger)));
1484+
let p2p_source = Arc::new(GossipSource::new_p2p(
1485+
Arc::clone(&network_graph),
1486+
Arc::clone(&chain_source),
1487+
Arc::clone(&runtime),
1488+
Arc::clone(&logger),
1489+
));
14861490

14871491
// Reset the RGS sync timestamp in case we somehow switch gossip sources
14881492
{
@@ -1597,19 +1601,14 @@ fn build_with_store_internal(
15971601
Arc::clone(&keys_manager),
15981602
));
15991603

1600-
let peer_manager_clone = Arc::clone(&peer_manager);
1601-
1604+
let peer_manager_clone = Arc::downgrade(&peer_manager);
16021605
hrn_resolver.register_post_queue_action(Box::new(move || {
1603-
peer_manager_clone.process_events();
1606+
if let Some(upgraded_pointer) = peer_manager_clone.upgrade() {
1607+
upgraded_pointer.process_events();
1608+
}
16041609
}));
16051610

1606-
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::clone(&peer_manager)));
1607-
1608-
gossip_source.set_gossip_verifier(
1609-
Arc::clone(&chain_source),
1610-
Arc::clone(&peer_manager),
1611-
Arc::clone(&runtime),
1612-
);
1611+
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
16131612

16141613
let connection_manager =
16151614
Arc::new(ConnectionManager::new(Arc::clone(&peer_manager), Arc::clone(&logger)));
@@ -1685,6 +1684,18 @@ fn build_with_store_internal(
16851684

16861685
let pathfinding_scores_sync_url = pathfinding_scores_sync_config.map(|c| c.url.clone());
16871686

1687+
#[cfg(cycle_tests)]
1688+
let mut _leak_checker = crate::LeakChecker(Vec::new());
1689+
#[cfg(cycle_tests)]
1690+
{
1691+
use std::any::Any;
1692+
use std::sync::Weak;
1693+
1694+
_leak_checker.0.push(Arc::downgrade(&channel_manager) as Weak<dyn Any + Send + Sync>);
1695+
_leak_checker.0.push(Arc::downgrade(&network_graph) as Weak<dyn Any + Send + Sync>);
1696+
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
1697+
}
1698+
16881699
Ok(Node {
16891700
runtime,
16901701
stop_sender,
@@ -1717,6 +1728,8 @@ fn build_with_store_internal(
17171728
om_mailbox,
17181729
async_payments_role,
17191730
hrn_resolver,
1731+
#[cfg(cycle_tests)]
1732+
_leak_checker,
17201733
})
17211734
}
17221735

src/gossip.rs

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::chain::ChainSource;
1717
use crate::config::RGS_SYNC_TIMEOUT_SECS;
1818
use crate::logger::{log_trace, LdkLogger, Logger};
1919
use crate::runtime::Runtime;
20-
use crate::types::{GossipSync, Graph, P2PGossipSync, PeerManager, RapidGossipSync, UtxoLookup};
20+
use crate::types::{GossipSync, Graph, P2PGossipSync, RapidGossipSync};
2121
use crate::Error;
2222

2323
pub(crate) enum GossipSource {
@@ -33,12 +33,15 @@ pub(crate) enum GossipSource {
3333
}
3434

3535
impl GossipSource {
36-
pub fn new_p2p(network_graph: Arc<Graph>, logger: Arc<Logger>) -> Self {
37-
let gossip_sync = Arc::new(P2PGossipSync::new(
38-
network_graph,
39-
None::<Arc<UtxoLookup>>,
40-
Arc::clone(&logger),
41-
));
36+
pub fn new_p2p(
37+
network_graph: Arc<Graph>, chain_source: Arc<ChainSource>, runtime: Arc<Runtime>,
38+
logger: Arc<Logger>,
39+
) -> Self {
40+
let verifier = chain_source.as_utxo_source().map(|utxo_source| {
41+
Arc::new(GossipVerifier::new(Arc::new(utxo_source), RuntimeSpawner::new(runtime)))
42+
});
43+
44+
let gossip_sync = Arc::new(P2PGossipSync::new(network_graph, verifier, logger));
4245
Self::P2PNetwork { gossip_sync }
4346
}
4447

@@ -62,27 +65,6 @@ impl GossipSource {
6265
}
6366
}
6467

65-
pub(crate) fn set_gossip_verifier(
66-
&self, chain_source: Arc<ChainSource>, peer_manager: Arc<PeerManager>,
67-
runtime: Arc<Runtime>,
68-
) {
69-
match self {
70-
Self::P2PNetwork { gossip_sync } => {
71-
if let Some(utxo_source) = chain_source.as_utxo_source() {
72-
let spawner = RuntimeSpawner::new(Arc::clone(&runtime));
73-
let gossip_verifier = Arc::new(GossipVerifier::new(
74-
Arc::new(utxo_source),
75-
spawner,
76-
Arc::clone(gossip_sync),
77-
peer_manager,
78-
));
79-
gossip_sync.add_utxo_lookup(Some(gossip_verifier));
80-
}
81-
},
82-
_ => (),
83-
}
84-
}
85-
8668
pub async fn update_rgs_snapshot(&self) -> Result<u32, Error> {
8769
match self {
8870
Self::P2PNetwork { gossip_sync: _, .. } => Ok(0),

src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ use std::default::Default;
110110
use std::net::ToSocketAddrs;
111111
use std::sync::{Arc, Mutex, RwLock};
112112
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
113+
#[cfg(cycle_tests)]
114+
use std::{any::Any, sync::Weak};
113115

114116
pub use balance::{BalanceDetails, LightningBalance, PendingSweepBalance};
115117
use bitcoin::secp256k1::PublicKey;
@@ -173,6 +175,23 @@ use crate::scoring::setup_background_pathfinding_scores_sync;
173175
#[cfg(feature = "uniffi")]
174176
uniffi::include_scaffolding!("ldk_node");
175177

178+
#[cfg(cycle_tests)]
179+
/// A list of [`Weak`]s which can be used to check that a [`Node`]'s inner fields are being
180+
/// properly released after the [`Node`] is dropped.
181+
pub struct LeakChecker(Vec<Weak<dyn Any + Send + Sync>>);
182+
183+
#[cfg(cycle_tests)]
184+
impl LeakChecker {
185+
/// Asserts that all the stored [`Weak`]s point to contents which have been freed.
186+
///
187+
/// This will (obviously) panic if the [`Node`] has not yet been dropped.
188+
pub fn assert_no_leaks(&self) {
189+
for weak in self.0.iter() {
190+
assert_eq!(weak.strong_count(), 0);
191+
}
192+
}
193+
}
194+
176195
/// The main interface object of LDK Node, wrapping the necessary LDK and BDK functionalities.
177196
///
178197
/// Needs to be initialized and instantiated through [`Builder::build`].
@@ -208,6 +227,8 @@ pub struct Node {
208227
om_mailbox: Option<Arc<OnionMessageMailbox>>,
209228
async_payments_role: Option<AsyncPaymentsRole>,
210229
hrn_resolver: Arc<HRNResolver>,
230+
#[cfg(cycle_tests)]
231+
_leak_checker: LeakChecker,
211232
}
212233

213234
impl Node {

src/liquidity.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
use std::collections::HashMap;
1111
use std::ops::Deref;
12-
use std::sync::{Arc, Mutex, RwLock};
12+
use std::sync::{Arc, Mutex, RwLock, Weak};
1313
use std::time::Duration;
1414

1515
use bitcoin::hashes::{sha256, Hash};
@@ -291,7 +291,7 @@ where
291291
lsps2_service: Option<LSPS2Service>,
292292
wallet: Arc<Wallet>,
293293
channel_manager: Arc<ChannelManager>,
294-
peer_manager: RwLock<Option<Arc<PeerManager>>>,
294+
peer_manager: RwLock<Option<Weak<PeerManager>>>,
295295
keys_manager: Arc<KeysManager>,
296296
liquidity_manager: Arc<LiquidityManager>,
297297
config: Arc<Config>,
@@ -302,7 +302,7 @@ impl<L: Deref> LiquiditySource<L>
302302
where
303303
L::Target: LdkLogger,
304304
{
305-
pub(crate) fn set_peer_manager(&self, peer_manager: Arc<PeerManager>) {
305+
pub(crate) fn set_peer_manager(&self, peer_manager: Weak<PeerManager>) {
306306
*self.peer_manager.write().unwrap() = Some(peer_manager);
307307
}
308308

@@ -715,8 +715,8 @@ where
715715
return;
716716
};
717717

718-
let init_features = if let Some(peer_manager) =
719-
self.peer_manager.read().unwrap().as_ref()
718+
let init_features = if let Some(Some(peer_manager)) =
719+
self.peer_manager.read().unwrap().as_ref().map(|weak| weak.upgrade())
720720
{
721721
// Fail if we're not connected to the prospective channel partner.
722722
if let Some(peer) = peer_manager.peer_by_node_id(&their_network_key) {

src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ pub(crate) type Scorer = CombinedScorer<Arc<Graph>, Arc<Logger>>;
254254

255255
pub(crate) type Graph = gossip::NetworkGraph<Arc<Logger>>;
256256

257-
pub(crate) type UtxoLookup = GossipVerifier<RuntimeSpawner, Arc<UtxoSourceClient>, Arc<Logger>>;
257+
pub(crate) type UtxoLookup = GossipVerifier<RuntimeSpawner, Arc<UtxoSourceClient>>;
258258

259259
pub(crate) type P2PGossipSync =
260260
lightning::routing::gossip::P2PGossipSync<Arc<Graph>, Arc<UtxoLookup>, Arc<Logger>>;

0 commit comments

Comments
 (0)