Skip to content

Commit 5173b2b

Browse files
committed
Clean up cancelled connection attempts
Clear per-peer connection state when the leading task is cancelled so later callers can retry and existing subscribers do not hang. Co-Authored-By: HAL 9000
1 parent 8304c3f commit 5173b2b

1 file changed

Lines changed: 73 additions & 14 deletions

File tree

src/connection.rs

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,44 @@ use crate::logger::{log_debug, log_error, log_info, LdkLogger};
1818
use crate::types::{KeysManager, PeerManager};
1919
use crate::Error;
2020

21+
type PendingConnections =
22+
Mutex<HashMap<PublicKey, Vec<tokio::sync::oneshot::Sender<Result<(), Error>>>>>;
23+
24+
struct PendingConnectionGuard<'a> {
25+
pending_connections: &'a PendingConnections,
26+
node_id: PublicKey,
27+
active: bool,
28+
}
29+
30+
impl<'a> PendingConnectionGuard<'a> {
31+
fn new(pending_connections: &'a PendingConnections, node_id: PublicKey) -> Self {
32+
Self { pending_connections, node_id, active: true }
33+
}
34+
35+
fn disarm(&mut self) {
36+
self.active = false;
37+
}
38+
}
39+
40+
impl Drop for PendingConnectionGuard<'_> {
41+
fn drop(&mut self) {
42+
if !self.active {
43+
return;
44+
}
45+
let mut pending_connections = self.pending_connections.lock().expect("lock");
46+
if let Some(subscribers) = pending_connections.remove(&self.node_id) {
47+
for subscriber in subscribers {
48+
let _ = subscriber.send(Err(Error::ConnectionFailed));
49+
}
50+
}
51+
}
52+
}
53+
2154
pub(crate) struct ConnectionManager<L: Deref + Clone + Sync + Send>
2255
where
2356
L::Target: LdkLogger,
2457
{
25-
pending_connections:
26-
Mutex<HashMap<PublicKey, Vec<tokio::sync::oneshot::Sender<Result<(), Error>>>>>,
58+
pending_connections: PendingConnections,
2759
peer_manager: Arc<PeerManager>,
2860
tor_proxy_config: Option<TorConfig>,
2961
keys_manager: Arc<KeysManager>,
@@ -60,26 +92,29 @@ where
6092
pub(crate) async fn do_connect_peer(
6193
&self, node_id: PublicKey, addr: SocketAddress,
6294
) -> Result<(), Error> {
95+
// If another task is already connecting, subscribe to its result instead of starting a
96+
// duplicate attempt.
97+
if let Some(pending_connection_ready_receiver) =
98+
self.register_or_subscribe_pending_connection(&node_id)
99+
{
100+
return pending_connection_ready_receiver.await.map_err(|e| {
101+
debug_assert!(false, "Failed to receive connection result: {:?}", e);
102+
log_error!(self.logger, "Failed to receive connection result: {:?}", e);
103+
Error::ConnectionFailed
104+
})?;
105+
}
106+
107+
let mut pending_connection =
108+
PendingConnectionGuard::new(&self.pending_connections, node_id);
63109
let res = self.do_connect_peer_internal(node_id, addr).await;
64110
self.propagate_result_to_subscribers(&node_id, res);
111+
pending_connection.disarm();
65112
res
66113
}
67114

68115
async fn do_connect_peer_internal(
69116
&self, node_id: PublicKey, addr: SocketAddress,
70117
) -> Result<(), Error> {
71-
// First, we check if there is already an outbound connection in flight, if so, we just
72-
// await on the corresponding watch channel. The task driving the connection future will
73-
// send us the result..
74-
let pending_ready_receiver_opt = self.register_or_subscribe_pending_connection(&node_id);
75-
if let Some(pending_connection_ready_receiver) = pending_ready_receiver_opt {
76-
return pending_connection_ready_receiver.await.map_err(|e| {
77-
debug_assert!(false, "Failed to receive connection result: {:?}", e);
78-
log_error!(self.logger, "Failed to receive connection result: {:?}", e);
79-
Error::ConnectionFailed
80-
})?;
81-
}
82-
83118
log_info!(self.logger, "Connecting to peer: {}@{}", node_id, addr);
84119

85120
match addr {
@@ -246,6 +281,7 @@ where
246281
match pending_connections_lock.entry(*node_id) {
247282
hash_map::Entry::Occupied(mut entry) => {
248283
let (tx, rx) = tokio::sync::oneshot::channel();
284+
entry.get_mut().retain(|subscriber| !subscriber.is_closed());
249285
entry.get_mut().push(tx);
250286
Some(rx)
251287
},
@@ -277,3 +313,26 @@ where
277313
}
278314
}
279315
}
316+
317+
#[cfg(test)]
318+
mod tests {
319+
use super::*;
320+
321+
#[test]
322+
fn pending_connection_guard_notifies_subscribers_when_abandoned() {
323+
let node_id: PublicKey =
324+
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".parse().unwrap();
325+
let pending_connections = Mutex::new(HashMap::new());
326+
let (sender, mut receiver) = tokio::sync::oneshot::channel();
327+
pending_connections.lock().expect("lock").insert(node_id, vec![sender]);
328+
329+
let connection_guard = PendingConnectionGuard::new(&pending_connections, node_id);
330+
drop(connection_guard);
331+
332+
assert!(
333+
pending_connections.lock().expect("lock").is_empty(),
334+
"abandoned connection attempt should remove pending state"
335+
);
336+
assert_eq!(receiver.try_recv(), Ok(Err(Error::ConnectionFailed)));
337+
}
338+
}

0 commit comments

Comments
 (0)