summaryrefslogtreecommitdiff
path: root/cli/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp')
-rw-r--r--cli/lsp/mod.rs7
-rw-r--r--cli/lsp/parent_process_checker.rs70
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);
+ }
+}