summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/flags.rs37
-rw-r--r--cli/lsp/mod.rs7
-rw-r--r--cli/lsp/parent_process_checker.rs70
-rw-r--r--cli/main.rs6
4 files changed, 112 insertions, 8 deletions
diff --git a/cli/flags.rs b/cli/flags.rs
index 295bdb317..9fc3103b4 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -84,7 +84,9 @@ pub enum DenoSubcommand {
root: Option<PathBuf>,
force: bool,
},
- Lsp,
+ Lsp {
+ parent_pid: Option<u32>,
+ },
Lint {
files: Vec<PathBuf>,
ignore: Vec<PathBuf>,
@@ -876,6 +878,16 @@ go-to-definition support and automatic code formatting.
How to connect various editors and IDEs to 'deno lsp':
https://deno.land/manual/getting_started/setup_your_environment#editors-and-ides")
+ .arg(
+ Arg::with_name("parent-pid")
+ .long("parent-pid")
+ .help("The parent process id to periodically check for the existence of or exit")
+ .takes_value(true)
+ .validator(|val: String| match val.parse::<usize>() {
+ Ok(_) => Ok(()),
+ Err(_) => Err("parent-pid should be a number".to_string()),
+ }),
+ )
}
fn lint_subcommand<'a, 'b>() -> App<'a, 'b> {
@@ -1621,8 +1633,11 @@ fn install_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
};
}
-fn lsp_parse(flags: &mut Flags, _matches: &clap::ArgMatches) {
- flags.subcommand = DenoSubcommand::Lsp;
+fn lsp_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
+ let parent_pid = matches
+ .value_of("parent-pid")
+ .map(|val| val.parse().unwrap());
+ flags.subcommand = DenoSubcommand::Lsp { parent_pid };
}
fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
@@ -2308,10 +2323,24 @@ mod tests {
assert_eq!(
r.unwrap(),
Flags {
- subcommand: DenoSubcommand::Lsp,
+ subcommand: DenoSubcommand::Lsp { parent_pid: None },
..Flags::default()
}
);
+
+ let r = flags_from_vec(svec!["deno", "lsp", "--parent-pid", "5"]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Lsp {
+ parent_pid: Some(5),
+ },
+ ..Flags::default()
+ }
+ );
+
+ let r = flags_from_vec(svec!["deno", "lsp", "--parent-pid", "invalid-arg"]);
+ assert!(r.is_err());
}
#[test]
diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs
index 367257911..4723f8b56 100644
--- a/cli/lsp/mod.rs
+++ b/cli/lsp/mod.rs
@@ -13,6 +13,7 @@ mod diagnostics;
mod documents;
pub(crate) mod language_server;
mod lsp_custom;
+mod parent_process_checker;
mod path_to_regex;
mod performance;
mod registries;
@@ -22,10 +23,14 @@ mod text;
mod tsc;
mod urls;
-pub async fn start() -> Result<(), AnyError> {
+pub async fn start(parent_pid: Option<u32>) -> Result<(), AnyError> {
let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout();
+ if let Some(parent_pid) = parent_pid {
+ parent_process_checker::start(parent_pid);
+ }
+
let (service, messages) =
LspService::new(language_server::LanguageServer::new);
Server::new(stdin, stdout)
diff --git a/cli/lsp/parent_process_checker.rs b/cli/lsp/parent_process_checker.rs
new file mode 100644
index 000000000..6c80969f4
--- /dev/null
+++ b/cli/lsp/parent_process_checker.rs
@@ -0,0 +1,70 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use tokio::time::sleep;
+use tokio::time::Duration;
+
+/// Starts a task that will check for the existence of the
+/// provided process id. Once that process no longer exists
+/// it will terminate the current process.
+pub fn start(parent_process_id: u32) {
+ tokio::task::spawn(async move {
+ loop {
+ sleep(Duration::from_secs(30)).await;
+
+ if !is_process_active(parent_process_id) {
+ eprintln!("Parent process lost. Exiting.");
+ std::process::exit(1);
+ }
+ }
+ });
+}
+
+#[cfg(unix)]
+fn is_process_active(process_id: u32) -> bool {
+ unsafe {
+ // signal of 0 checks for the existence of the process id
+ libc::kill(process_id as i32, 0) == 0
+ }
+}
+
+#[cfg(windows)]
+fn is_process_active(process_id: u32) -> bool {
+ use winapi::shared::minwindef::DWORD;
+ use winapi::shared::minwindef::FALSE;
+ use winapi::shared::ntdef::NULL;
+ use winapi::shared::winerror::WAIT_TIMEOUT;
+ use winapi::um::handleapi::CloseHandle;
+ use winapi::um::processthreadsapi::OpenProcess;
+ use winapi::um::synchapi::WaitForSingleObject;
+ use winapi::um::winnt::SYNCHRONIZE;
+
+ unsafe {
+ let process = OpenProcess(SYNCHRONIZE, FALSE, process_id as DWORD);
+ let result = if process == NULL {
+ false
+ } else {
+ WaitForSingleObject(process, 0) == WAIT_TIMEOUT
+ };
+ CloseHandle(process);
+ result
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::is_process_active;
+ use std::process::Command;
+ use test_util::deno_exe_path;
+
+ #[test]
+ fn process_active() {
+ // launch a long running process
+ let mut child = Command::new(deno_exe_path()).arg("lsp").spawn().unwrap();
+
+ let pid = child.id();
+ assert_eq!(is_process_active(pid), true);
+ child.kill().unwrap();
+ child.wait().unwrap();
+ assert_eq!(is_process_active(pid), false);
+ }
+}
diff --git a/cli/main.rs b/cli/main.rs
index 3354f4399..9941d737a 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -470,8 +470,8 @@ async fn install_command(
tools::installer::install(flags, &module_url, args, name, root, force)
}
-async fn lsp_command() -> Result<(), AnyError> {
- lsp::start().await
+async fn lsp_command(parent_pid: Option<u32>) -> Result<(), AnyError> {
+ lsp::start(parent_pid).await
}
async fn lint_command(
@@ -1264,7 +1264,7 @@ fn get_subcommand(
} => {
install_command(flags, module_url, args, name, root, force).boxed_local()
}
- DenoSubcommand::Lsp => lsp_command().boxed_local(),
+ DenoSubcommand::Lsp { parent_pid } => lsp_command(parent_pid).boxed_local(),
DenoSubcommand::Lint {
files,
rules,