Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ impl TerminalService {
.collect::<Vec<_>>();
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());
}
Expand Down Expand Up @@ -645,6 +648,7 @@ enum ActorMessage {
ReaderFailed(String),
WriterFailed(String),
ChildExited(Result<Option<u32>, String>),
#[cfg(not(windows))]
RefreshForegroundProcess {
reply: std_mpsc::SyncSender<Result<()>>,
},
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -1193,6 +1198,7 @@ fn receive_actor_message(
Ok(ActorMessage::Output(bytes))
}

#[cfg(not(windows))]
fn publish_foreground_process(
shutdown: &Receiver<()>,
master: &dyn MasterPty,
Expand Down Expand Up @@ -1257,7 +1263,7 @@ fn foreground_process(master: &dyn MasterPty) -> Option<String> {
.map(|process| process.name.clone())
}

#[cfg(not(target_os = "linux"))]
#[cfg(not(any(windows, target_os = "linux")))]
fn foreground_process(_master: &dyn MasterPty) -> Option<String> {
None
}
Expand Down Expand Up @@ -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] {
Expand Down
35 changes: 7 additions & 28 deletions tests/windows-daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Daemon {
(stream, response)
}

fn create(&self, fixture: &Fixture) -> TerminalInfo {
fn create(&self, request: CreateTerminal) -> TerminalInfo {
let CreateTerminal {
program,
args,
Expand All @@ -92,7 +92,7 @@ impl Daemon {
env,
cols,
rows,
} = fixture.request();
} = request;
match self.request(Request::Create {
program,
args,
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
Loading