Limit language server reinstallation attempts (#3177)

Release Notes:

- Fixed a case where a sufficiently broken language server system
configuration could cause an infinite repeated server reinstallation,
consuming resources indefinitely.
This commit is contained in:
Julia 2023-10-26 17:20:13 +02:00 committed by GitHub
commit 832026a0a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 17 deletions

View file

@ -38,7 +38,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
str, str,
sync::{ sync::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
Arc, Arc,
}, },
}; };
@ -115,6 +115,7 @@ pub struct CachedLspAdapter {
pub disk_based_diagnostics_progress_token: Option<String>, pub disk_based_diagnostics_progress_token: Option<String>,
pub language_ids: HashMap<String, String>, pub language_ids: HashMap<String, String>,
pub adapter: Arc<dyn LspAdapter>, pub adapter: Arc<dyn LspAdapter>,
pub reinstall_attempt_count: AtomicU64,
} }
impl CachedLspAdapter { impl CachedLspAdapter {
@ -133,6 +134,7 @@ impl CachedLspAdapter {
disk_based_diagnostics_progress_token, disk_based_diagnostics_progress_token,
language_ids, language_ids,
adapter, adapter,
reinstall_attempt_count: AtomicU64::new(0),
}) })
} }

View file

@ -91,6 +91,8 @@ pub use fs::*;
pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX; pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX;
pub use worktree::*; pub use worktree::*;
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
pub trait Item { pub trait Item {
fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>; fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>; fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
@ -2722,6 +2724,10 @@ impl Project {
language: Arc<Language>, language: Arc<Language>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
if adapter.reinstall_attempt_count.load(SeqCst) > MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
return;
}
let key = (worktree_id, adapter.name.clone()); let key = (worktree_id, adapter.name.clone());
if self.language_server_ids.contains_key(&key) { if self.language_server_ids.contains_key(&key) {
return; return;
@ -2772,11 +2778,21 @@ impl Project {
} }
Err(err) => { Err(err) => {
log::error!("failed to start language server {:?}: {}", server_name, err); log::error!("failed to start language server {server_name:?}: {err}");
log::error!("server stderr: {:?}", stderr_capture.lock().take()); log::error!("server stderr: {:?}", stderr_capture.lock().take());
if let Some(this) = this.upgrade(&cx) { let this = this.upgrade(&cx)?;
if let Some(container_dir) = container_dir { let container_dir = container_dir?;
let attempt_count = adapter.reinstall_attempt_count.fetch_add(1, SeqCst);
if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
log::error!(
"Hit {max} max reinstallation attempts for {server_name:?}"
);
return None;
}
let installation_test_binary = adapter let installation_test_binary = adapter
.installation_test_binary(container_dir.to_path_buf()) .installation_test_binary(container_dir.to_path_buf())
.await; .await;
@ -2790,8 +2806,6 @@ impl Project {
cx, cx,
) )
}); });
}
}
None None
} }