terminal: Kill the terminal child process, not the terminal process on exit (#41631)

Lukas Wirth created

When rerunning a task, our process id fetching seems to sometimes return
the previous terminal's process id when respawning the task, causing us
to kill the new terminal once the previous one drops as we spawn a new
one, then drop the old one. This results in rerun sometimes spawning a
blank task as the terminal immediately exits. The fix here is simple, we
actually want to kill the process running inside the terminal process,
not the terminal process itself when we exit in the terminal.

No relnotes as this was introduced yesterday in
https://github.com/zed-industries/zed/pull/41562

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/terminal/src/pty_info.rs           | 23 +++++++++++++++--------
crates/terminal/src/terminal.rs           |  2 +-
crates/terminal_view/src/terminal_view.rs |  3 ++-
3 files changed, 18 insertions(+), 10 deletions(-)

Detailed changes

crates/terminal/src/pty_info.rs 🔗

@@ -15,6 +15,12 @@ pub struct ProcessIdGetter {
     fallback_pid: u32,
 }
 
+impl ProcessIdGetter {
+    pub fn fallback_pid(&self) -> Pid {
+        Pid::from_u32(self.fallback_pid)
+    }
+}
+
 #[cfg(unix)]
 impl ProcessIdGetter {
     fn new(pty: &Pty) -> ProcessIdGetter {
@@ -31,10 +37,6 @@ impl ProcessIdGetter {
         }
         Some(Pid::from_u32(pid as u32))
     }
-
-    pub fn fallback_pid(&self) -> u32 {
-        self.fallback_pid
-    }
 }
 
 #[cfg(windows)]
@@ -66,10 +68,6 @@ impl ProcessIdGetter {
         }
         Some(Pid::from_u32(pid))
     }
-
-    pub fn fallback_pid(&self) -> u32 {
-        self.fallback_pid
-    }
 }
 
 #[derive(Clone, Debug)]
@@ -122,10 +120,19 @@ impl PtyProcessInfo {
         }
     }
 
+    fn get_child(&self) -> Option<&Process> {
+        let pid = self.pid_getter.fallback_pid();
+        self.system.process(pid)
+    }
+
     pub(crate) fn kill_current_process(&mut self) -> bool {
         self.refresh().is_some_and(|process| process.kill())
     }
 
+    pub(crate) fn kill_child_process(&mut self) -> bool {
+        self.get_child().is_some_and(|process| process.kill())
+    }
+
     fn load(&mut self) -> Option<ProcessInfo> {
         let process = self.refresh()?;
         let cwd = process.cwd().map_or(PathBuf::new(), |p| p.to_owned());

crates/terminal/src/terminal.rs 🔗

@@ -2237,7 +2237,7 @@ unsafe fn append_text_to_term(term: &mut Term<ZedListener>, text_lines: &[&str])
 impl Drop for Terminal {
     fn drop(&mut self) {
         if let TerminalType::Pty { pty_tx, info } = &mut self.terminal_type {
-            info.kill_current_process();
+            info.kill_child_process();
             pty_tx.0.send(Msg::Shutdown).ok();
         }
     }

crates/terminal_view/src/terminal_view.rs 🔗

@@ -1141,7 +1141,8 @@ impl Item for TerminalView {
         let pid = terminal.pid_getter()?.fallback_pid();
 
         Some(TabTooltipContent::Custom(Box::new(move |_window, cx| {
-            cx.new(|_| TerminalTooltip::new(title.clone(), pid)).into()
+            cx.new(|_| TerminalTooltip::new(title.clone(), pid.as_u32()))
+                .into()
         })))
     }