From 78b13acfba23d571ea7585599fd477ee78f0875a Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Fri, 12 Jun 2026 19:38:25 +0300 Subject: [PATCH] perf: reduce pty actor idle poll frequency --- src/pty/actor/unix.rs | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/pty/actor/unix.rs b/src/pty/actor/unix.rs index e25d3dcb..bf463005 100644 --- a/src/pty/actor/unix.rs +++ b/src/pty/actor/unix.rs @@ -12,10 +12,11 @@ use tracing::{debug, warn}; use crate::pty::fd; -#[cfg(not(test))] -const ACTOR_POLL_MS: i32 = 50; -#[cfg(test)] -const ACTOR_POLL_MS: i32 = 1000; +// Actor handle methods must call wake_actor() after queuing work. The idle +// timeout is only a fallback for missed wakes; PTY and wake readiness drive +// normal responsiveness. +const ACTOR_IDLE_POLL_MS: i32 = 1000; +const ACTOR_WRITE_READY_POLL_MS: i32 = 50; const ACTOR_COMMAND_BUFFER: usize = 1024; const HANDOFF_DRAIN_TIMEOUT: Duration = Duration::from_secs(2); @@ -437,7 +438,7 @@ impl PtyIoActorRunner { self.wake_read_fd.as_raw_fd(), self.state == ActorState::Running, !self.pending_writes.is_empty(), - ACTOR_POLL_MS, + ACTOR_IDLE_POLL_MS, ) { Ok(readiness) => { if readiness.wake_ready { @@ -666,7 +667,7 @@ impl PtyIoActorRunner { } } Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { - let _ = fd::poll_write_ready(self.file.as_raw_fd(), ACTOR_POLL_MS); + let _ = fd::poll_write_ready(self.file.as_raw_fd(), ACTOR_WRITE_READY_POLL_MS); return; } Err(err) if err.kind() == std::io::ErrorKind::Interrupted => return, @@ -873,6 +874,31 @@ mod tests { handle.shutdown(); } + #[test] + fn actor_wakes_idle_poll_for_handoff_control() { + let (poll_tx, poll_rx) = std_mpsc::channel(); + let (handle, _peer, _read_rx) = + actor_with_socket_pair_and_poll_observer(false, Some(poll_tx)); + poll_rx + .recv_timeout(Duration::from_secs(1)) + .expect("actor entered idle poll"); + + let start = Instant::now(); + let handoff_handle = handle.clone(); + let handoff = + std::thread::spawn(move || handoff_handle.begin_handoff(Duration::from_secs(1))); + + handoff + .join() + .expect("handoff thread joins") + .expect("handoff control should wake idle actor"); + assert!( + start.elapsed() < Duration::from_millis(500), + "handoff control should be driven by wake fd, not the idle poll timeout" + ); + handle.shutdown(); + } + #[test] fn poll_ignores_pty_hup_without_pty_interest() { let (actor_socket, peer) = UnixStream::pair().expect("socket pair");