Files
Aether/apps/aether-tunnel/src/tunnel/task.rs
T
elky ec95f2ca1f fix(tunnel): prevent stream stalls and harden session cleanup
Reliably deliver flow-control credits and terminal states, isolate slow streams and heartbeats, negotiate stream windows, and clean up cancelled streams and session tasks.

Add regression coverage for queue pressure, early cancellation, small-window streaming, drain, and reconnect. Validate 185 agent tests, 88 gateway tunnel tests, and 21 protocol tests.
2026-09-07 15:39:40 +08:00

53 lines
1.2 KiB
Rust

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::task::{JoinError, JoinHandle};
pub(super) struct SessionTask<T>(JoinHandle<T>);
impl<T> SessionTask<T> {
pub(super) fn new(handle: JoinHandle<T>) -> Self {
Self(handle)
}
pub(super) fn abort(&self) {
self.0.abort();
}
pub(super) fn is_finished(&self) -> bool {
self.0.is_finished()
}
}
impl<T> Future for SessionTask<T> {
type Output = Result<T, JoinError>;
fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.0).poll(context)
}
}
impl<T> Drop for SessionTask<T> {
fn drop(&mut self) {
self.0.abort();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn dropping_a_session_task_aborts_its_child() {
let child = tokio::spawn(std::future::pending::<()>());
let abort = child.abort_handle();
drop(SessionTask::new(child));
tokio::time::timeout(std::time::Duration::from_secs(1), async {
while !abort.is_finished() {
tokio::task::yield_now().await;
}
})
.await
.unwrap();
}
}