Release handlers on close to break session reference cycles
Handlers commonly capture a clone of their own Session, and the Session owns its handlers, so a closed session was never freed. On shutdown, take the on_close handler instead of cloning it and clear the request and notification handlers after it runs. Bump to 0.2.1. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -450,3 +450,43 @@ async fn axum_adapter_serves_sessions_next_to_http_routes() {
|
||||
tcp.read_to_string(&mut body).await.unwrap();
|
||||
assert!(body.starts_with("HTTP/1.1 200") && body.ends_with("ok"), "{body}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn closing_releases_handlers_that_capture_the_session() {
|
||||
let client = connect(start_server().await).await;
|
||||
let captured = Arc::new(());
|
||||
|
||||
// Handlers typically hold a clone of their own session; that must not keep
|
||||
// the session (and everything the handlers capture) alive after close.
|
||||
client
|
||||
.on_request::<Silent, _>({
|
||||
let session = client.clone();
|
||||
let captured = captured.clone();
|
||||
move |_, ()| {
|
||||
let _ = (&session, &captured);
|
||||
async { Ok(()) }
|
||||
}
|
||||
})
|
||||
.await;
|
||||
client
|
||||
.on_close({
|
||||
let session = client.clone();
|
||||
let captured = captured.clone();
|
||||
move || {
|
||||
let _ = (&session, &captured);
|
||||
async { Ok(()) }
|
||||
}
|
||||
})
|
||||
.await;
|
||||
assert_eq!(Arc::strong_count(&captured), 3);
|
||||
|
||||
client.close().await.unwrap();
|
||||
|
||||
tokio::time::timeout(WAIT, async {
|
||||
while Arc::strong_count(&captured) > 1 {
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("handlers were not released after close");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user