From 29536261f66a18330abeaea37b54afac4e184f35 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Wed, 17 Dec 2025 14:23:35 +0200 Subject: [PATCH 1/3] fix: always double deref Connection -> ConnectionInner --- quinn/src/connection.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/quinn/src/connection.rs b/quinn/src/connection.rs index e40dd0c1f..f1c882799 100644 --- a/quinn/src/connection.rs +++ b/quinn/src/connection.rs @@ -53,7 +53,7 @@ impl Connecting { let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel(); let (on_connected_send, on_connected_recv) = oneshot::channel(); - let conn = ConnectionRef(Arc::new(ConnectionInner { + let conn = ConnectionRef(Arc::new(Arc::new(ConnectionInner { state: Mutex::new(State::new( conn, handle, @@ -65,7 +65,7 @@ impl Connecting { runtime.clone(), )), shared: Shared::default(), - })); + }))); let driver = ConnectionDriver(conn.clone()); runtime.spawn(Box::pin( @@ -1222,10 +1222,10 @@ impl Future for OnClosed { } #[derive(Debug)] -pub(crate) struct ConnectionRef(Arc); +pub(crate) struct ConnectionRef(Arc>); impl ConnectionRef { - fn from_arc(inner: Arc) -> Self { + fn from_arc(inner: Arc>) -> Self { inner.state.lock("from_arc").ref_count += 1; Self(inner) } @@ -1275,7 +1275,7 @@ pub(crate) struct ConnectionInner { /// This contains a weak reference to the connection so will not itself keep the connection /// alive. #[derive(Debug, Clone)] -pub struct WeakConnectionHandle(Weak); +pub struct WeakConnectionHandle(Weak>); impl WeakConnectionHandle { /// Returns `true` if the [`Connection`] associated with this handle is still alive. From 81ea13cb2930ce88f02e440452e17e6fd3e949af Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 5 Jan 2026 13:55:41 +0100 Subject: [PATCH 2/3] clippy --- quinn/src/connection.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quinn/src/connection.rs b/quinn/src/connection.rs index f1c882799..7aad43b4c 100644 --- a/quinn/src/connection.rs +++ b/quinn/src/connection.rs @@ -1222,9 +1222,11 @@ impl Future for OnClosed { } #[derive(Debug)] +#[allow(clippy::redundant_allocation)] pub(crate) struct ConnectionRef(Arc>); impl ConnectionRef { + #[allow(clippy::redundant_allocation)] fn from_arc(inner: Arc>) -> Self { inner.state.lock("from_arc").ref_count += 1; Self(inner) From e4cea98529ec014fe7ed676766cef663c132b731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cramfox=E2=80=9D?= <“kasey@n0.computer”> Date: Thu, 8 Jan 2026 20:41:15 -0500 Subject: [PATCH 3/3] test to ensure double ref does not immediately drop the inner connection --- quinn/src/tests.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/quinn/src/tests.rs b/quinn/src/tests.rs index f286cf3cd..0e8db1895 100755 --- a/quinn/src/tests.rs +++ b/quinn/src/tests.rs @@ -1118,3 +1118,37 @@ async fn on_closed_endpoint_drop() { .expect("client timeout") .expect("client task panicked"); } + +#[tokio::test] +async fn weak_connection_handle() { + let _guard = subscribe(); + let endpoint = endpoint(); + let endpoint2 = endpoint.clone(); + let server_task = tokio::spawn(async move { + let conn = endpoint2 + .accept() + .await + .expect("endpoint") + .await + .expect("connection"); + // create a weak handle to the connection + // ensure the underlying connection is not immediately dropped + let weak = conn.weak_handle(); + assert!(weak.is_alive()); + drop(conn); + // wait to ensure the connection is fully cleaned up + endpoint2.wait_idle().await; + assert!(!weak.is_alive()); + }); + let client_task = tokio::spawn(async move { + let conn = endpoint + .connect(endpoint.local_addr().unwrap(), "localhost") + .unwrap() + .await + .expect("connect"); + conn.on_closed().await; + }); + let (server_res, client_res) = tokio::join!(server_task, client_task); + server_res.expect("server task panicked"); + client_res.expect("client task panicked"); +}