diff options
Diffstat (limited to 'cli/lsp')
-rw-r--r-- | cli/lsp/mod.rs | 7 | ||||
-rw-r--r-- | cli/lsp/parent_process_checker.rs | 70 |
2 files changed, 76 insertions, 1 deletions
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); + } +} |