diff --git a/src/service.rs b/src/service.rs index 09f2049..080c5ab 100644 --- a/src/service.rs +++ b/src/service.rs @@ -224,6 +224,9 @@ impl TerminalService { .collect::>(); let mut terminals = Vec::with_capacity(handles.len()); for terminal in handles { + // Windows has no foreground query; do not wait behind actor + // backpressure just to read its already-cached metadata. + #[cfg(not(windows))] terminal.request(|reply| ActorMessage::RefreshForegroundProcess { reply })?; terminals.push(terminal.info()); } @@ -645,6 +648,7 @@ enum ActorMessage { ReaderFailed(String), WriterFailed(String), ChildExited(Result, String>), + #[cfg(not(windows))] RefreshForegroundProcess { reply: std_mpsc::SyncSender>, }, @@ -796,6 +800,7 @@ fn run_actor(config: ActorConfig) -> Result<()> { let result = loop { match receive_actor_message(&messages, &mut pending_message, &shutdown) { + #[cfg(not(windows))] Ok(ActorMessage::RefreshForegroundProcess { reply }) => { if let Ok(master) = master.get() { publish_foreground_process( @@ -1193,6 +1198,7 @@ fn receive_actor_message( Ok(ActorMessage::Output(bytes)) } +#[cfg(not(windows))] fn publish_foreground_process( shutdown: &Receiver<()>, master: &dyn MasterPty, @@ -1257,7 +1263,7 @@ fn foreground_process(master: &dyn MasterPty) -> Option { .map(|process| process.name.clone()) } -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(windows, target_os = "linux")))] fn foreground_process(_master: &dyn MasterPty) -> Option { None } @@ -1698,6 +1704,22 @@ mod tests { ) } + #[test] + #[cfg(windows)] + fn windows_list_reads_metadata_without_an_actor_request() { + let (actor, _writer) = test_actor(); + actor.shutdown().unwrap(); + let service = TerminalService::default(); + service.terminals.lock().unwrap().insert(1, Arc::new(actor)); + + // Even a closed actor channel is irrelevant to cached Windows metadata. + let terminals = service.list().unwrap(); + assert_eq!(terminals.len(), 1); + assert_eq!(terminals[0].id, 1); + assert_eq!(terminals[0].title, "test"); + assert!(terminals[0].foreground_process.is_none()); + } + #[test] fn child_exit_and_reader_eof_are_distinct_actor_signals() { for eof_first in [false, true] { diff --git a/tests/windows-daemon.rs b/tests/windows-daemon.rs index 1a4e1f3..7ab8147 100644 --- a/tests/windows-daemon.rs +++ b/tests/windows-daemon.rs @@ -82,7 +82,7 @@ impl Daemon { (stream, response) } - fn create(&self, fixture: &Fixture) -> TerminalInfo { + fn create(&self, request: CreateTerminal) -> TerminalInfo { let CreateTerminal { program, args, @@ -92,7 +92,7 @@ impl Daemon { env, cols, rows, - } = fixture.request(); + } = request; match self.request(Request::Create { program, args, @@ -318,28 +318,7 @@ fn named_pipe_daemon_create_input_output_resize_and_shutdown() { let executable = daemon.root.executable("daemon fixture.exe"); let mut request = fixture.request(); request.program = executable.to_str().unwrap().into(); - let CreateTerminal { - program, - args, - cwd, - title, - group_id, - env, - cols, - rows, - } = request; - let Response::Created { terminal } = daemon.request(Request::Create { - program, - args, - cwd, - title, - group_id, - env, - cols, - rows, - }) else { - panic!("create response"); - }; + let terminal = daemon.create(request); let mut child = fixture.connect(); let mut subscription = daemon.subscribe(terminal.id, AttachmentRole::Observer); child.command(FixtureCommand::Output("\x1b[2J\x1b[Hdaemon-output".into())); @@ -395,7 +374,7 @@ fn owner_loss_cancels_blocked_subscriber_and_partial_request() { let (owner, response) = daemon.own(None); assert!(matches!(response, Response::Owned)); let fixture = Fixture::new(); - let terminal = daemon.create(&fixture); + let terminal = daemon.create(fixture.request()); let mut child = fixture.connect(); // SAFETY: open the live child for waiting; PID is only used to obtain a // stable process handle for this assertion, not as terminal identity. @@ -422,7 +401,7 @@ fn natural_exit_delivers_contiguous_output_and_retains_final_state() { let (owner, response) = daemon.own(None); assert!(matches!(response, Response::Owned)); let fixture = Fixture::new(); - let terminal = daemon.create(&fixture); + let terminal = daemon.create(fixture.request()); let mut child = fixture.connect(); // Capture a stable process handle before exit; the PID is not terminal identity. let process = unsafe { @@ -576,8 +555,8 @@ fn observer_disconnect_and_control_input_preserve_independent_terminals() { assert!(matches!(response, Response::Owned)); let first_fixture = Fixture::new(); let second_fixture = Fixture::new(); - let first = daemon.create(&first_fixture); - let second = daemon.create(&second_fixture); + let first = daemon.create(first_fixture.request()); + let second = daemon.create(second_fixture.request()); assert_ne!(first.id, second.id); let mut first_child = first_fixture.connect(); let mut second_child = second_fixture.connect();