debugger: Fix issues with restarting sessions (#33932)

Restarting sessions was broken in #33273 when we moved away from calling
`kill` in the shutdown sequence. This PR re-adds that `kill` call so
that old debug adapter processes will be cleaned up when sessions are
restarted within Zed. This doesn't re-introduce the issue that motivated
the original changes to the shutdown sequence, because we still send
Disconnect/Terminate to debug adapters when quitting Zed without killing
the process directly.

We also now remove manually-restarted sessions eagerly from the session
list.

Closes #33916 

Release Notes:

- debugger: Fixed not being able to restart sessions for Debugpy and
other adapters that communicate over TCP.
- debugger: Fixed debug adapter processes not being cleaned up.

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
This commit is contained in:
Cole Miller 2025-07-07 14:28:59 -04:00 committed by GitHub
parent 2a6ef006f4
commit e0c860c42a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 82 additions and 62 deletions

View file

@ -677,6 +677,7 @@ pub struct Session {
ignore_breakpoints: bool,
exception_breakpoints: BTreeMap<String, (ExceptionBreakpointsFilter, IsEnabled)>,
background_tasks: Vec<Task<()>>,
restart_task: Option<Task<()>>,
task_context: TaskContext,
}
@ -838,6 +839,7 @@ impl Session {
loaded_sources: Vec::default(),
threads: IndexMap::default(),
background_tasks: Vec::default(),
restart_task: None,
locations: Default::default(),
is_session_terminated: false,
ignore_breakpoints: false,
@ -1870,18 +1872,30 @@ impl Session {
}
pub fn restart(&mut self, args: Option<Value>, cx: &mut Context<Self>) {
if self.capabilities.supports_restart_request.unwrap_or(false) && !self.is_terminated() {
self.request(
RestartCommand {
raw: args.unwrap_or(Value::Null),
},
Self::fallback_to_manual_restart,
cx,
)
.detach();
} else {
cx.emit(SessionStateEvent::Restart);
if self.restart_task.is_some() || self.as_running().is_none() {
return;
}
let supports_dap_restart =
self.capabilities.supports_restart_request.unwrap_or(false) && !self.is_terminated();
self.restart_task = Some(cx.spawn(async move |this, cx| {
let _ = this.update(cx, |session, cx| {
if supports_dap_restart {
session
.request(
RestartCommand {
raw: args.unwrap_or(Value::Null),
},
Self::fallback_to_manual_restart,
cx,
)
.detach();
} else {
cx.emit(SessionStateEvent::Restart);
}
});
}));
}
pub fn shutdown(&mut self, cx: &mut Context<Self>) -> Task<()> {
@ -1919,8 +1933,13 @@ impl Session {
cx.emit(SessionStateEvent::Shutdown);
cx.spawn(async move |_, _| {
cx.spawn(async move |this, cx| {
task.await;
let _ = this.update(cx, |this, _| {
if let Some(adapter_client) = this.adapter_client() {
adapter_client.kill();
}
});
})
}