Implement build cancellation (#888)

- Add `texlab.cancelBuild` workspace command to cancel all currently active builds.
- Cancelled builds now return the previously unused `CANCELLED` status.
- Fixes #887.
This commit is contained in:
Patrick Förster 2023-05-20 20:56:23 +02:00 committed by GitHub
parent 2b319fc0d4
commit 68bf2b3d96
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 18 deletions

View file

@ -12,6 +12,7 @@ base-db = { path = "../base-db" }
bstr = "1.4.0"
crossbeam-channel = "0.5.8"
itertools = "0.10.5"
libc = "0.2.144"
log = "0.4.17"
rowan = "0.15.11"
rustc-hash = "1.1.0"

View file

@ -1,7 +1,7 @@
use std::{
io::{BufReader, Read},
path::{Path, PathBuf},
process::{ExitStatus, Stdio},
process::{Child, Stdio},
thread::{self, JoinHandle},
};
@ -64,7 +64,7 @@ impl BuildCommand {
})
}
pub fn run(self, sender: Sender<String>) -> Result<ExitStatus, BuildError> {
pub fn spawn(self, sender: Sender<String>) -> Result<Child, BuildError> {
log::debug!(
"Spawning compiler {} {:#?} in directory {}",
self.program,
@ -72,19 +72,56 @@ impl BuildCommand {
self.working_dir.display()
);
let mut process = std::process::Command::new(&self.program)
.args(self.args)
let mut process = self.spawn_internal()?;
track_output(process.stderr.take().unwrap(), sender.clone());
track_output(process.stdout.take().unwrap(), sender);
Ok(process)
}
#[cfg(windows)]
fn spawn_internal(&self) -> Result<Child, BuildError> {
std::process::Command::new(&self.program)
.args(self.args.clone())
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.current_dir(&self.working_dir)
.spawn()?;
.spawn()
.map_err(Into::into)
}
track_output(process.stderr.take().unwrap(), sender.clone());
track_output(process.stdout.take().unwrap(), sender);
#[cfg(unix)]
fn spawn_internal(&self) -> Result<Child, BuildError> {
use std::os::unix::process::CommandExt;
std::process::Command::new(&self.program)
.args(self.args.clone())
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.current_dir(&self.working_dir)
.process_group(0)
.spawn()
.map_err(Into::into)
}
let status = process.wait();
Ok(status?)
#[cfg(windows)]
pub fn cancel(pid: u32) -> std::io::Result<bool> {
Ok(std::process::Command::new("taskkill")
.arg("/PID")
.arg(pid.to_string())
.arg("/F")
.arg("/T")
.status()?
.success())
}
#[cfg(not(windows))]
pub fn cancel(pid: u32) -> Result<bool> {
unsafe {
libc::killpg(pid as libc::pid_t, libc::SIGTERM);
}
Ok(true)
}
}