From e49e53e4bc78788aa554d01f9c1a89773b431aec Mon Sep 17 00:00:00 2001 From: IntellyCode <47359527+IntellyCode@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:51:20 +0300 Subject: [PATCH] Close a dropped TCP session without blocking on its task `IpStackTcpStream::drop` waited for the session's task to finish, through `block_in_place` and `block_on`. `block_in_place` works by handing the worker's core to the blocking pool, and the pool stops accepting work once the runtime begins shutting down. A session dropped from that point on blocks its worker forever: the core is never handed off, the task it waits for has no thread left to run on, and the runtime's own shutdown waits for that worker in turn. The farewell packet reaches the device through an unbounded channel, so it is sent from the drop itself. The task is told to exit through the channel it already watches, and aborted rather than awaited. Aborting drops the task's `destroy_messenger`, and the watcher that removes the session treats a dropped sender the same as a sent one, so the session still leaves the stack. --- src/stream/tcp.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index c545978..246ef14 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -434,11 +434,22 @@ impl Drop for IpStackTcpStream { log::trace!("{nt} {state:?}: [drop] session dropping, ========================= "); if let Some(task_handle) = self.task_handle.take() { if !task_handle.is_finished() { + // The farewell packet reaches the device through an unbounded channel, so it + // is sent here rather than left to the task. + { + let mut tcb = self.tcb.lock().unwrap(); + if let Err(e) = send_fin_n_change_state_to_fin_wait1("[drop]", nt, &self.up_packet_sender, &mut tcb) { + log::debug!("{nt} {state:?}: [drop] cannot send the farewell packet: {e}"); + } + } if let Some(notifier) = self.exit_notifier.take() { - _ = tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(notifier.send(()))); + // The channel holds ten slots and one signal ends the task, so the send + // needs no runtime of its own. + _ = notifier.try_send(()); } - // synchronously wait for the task to finish - _ = tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(task_handle)); + // Dropping the task drops its `destroy_messenger`, which wakes the watcher + // that removes this session from the stack. + task_handle.abort(); } else { log::trace!("{nt} {state:?}: [drop] task already finished, no need to wait exiting"); }