Reap overly long LSP requests with a 2m timeout

Co-authored-by: Julia Risley <julia@zed.dev>
This commit is contained in:
Kirill Bulatov 2023-08-30 18:41:41 +03:00
parent e3a0252b04
commit 7e5735c8f1

View file

@ -4,7 +4,7 @@ pub use lsp_types::*;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use collections::HashMap; use collections::HashMap;
use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite}; use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite, FutureExt};
use gpui::{executor, AsyncAppContext, Task}; use gpui::{executor, AsyncAppContext, Task};
use parking_lot::Mutex; use parking_lot::Mutex;
use postage::{barrier, prelude::Stream}; use postage::{barrier, prelude::Stream};
@ -26,12 +26,14 @@ use std::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak, Arc, Weak,
}, },
time::{Duration, Instant},
}; };
use std::{path::Path, process::Stdio}; use std::{path::Path, process::Stdio};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
const JSON_RPC_VERSION: &str = "2.0"; const JSON_RPC_VERSION: &str = "2.0";
const CONTENT_LEN_HEADER: &str = "Content-Length: "; const CONTENT_LEN_HEADER: &str = "Content-Length: ";
const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2);
type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>; type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>)>; type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>)>;
@ -697,7 +699,7 @@ impl LanguageServer {
outbound_tx: &channel::Sender<String>, outbound_tx: &channel::Sender<String>,
executor: &Arc<executor::Background>, executor: &Arc<executor::Background>,
params: T::Params, params: T::Params,
) -> impl 'static + Future<Output = Result<T::Result>> ) -> impl 'static + Future<Output = anyhow::Result<T::Result>>
where where
T::Result: 'static + Send, T::Result: 'static + Send,
{ {
@ -738,10 +740,25 @@ impl LanguageServer {
.try_send(message) .try_send(message)
.context("failed to write to language server's stdin"); .context("failed to write to language server's stdin");
let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
let started = Instant::now();
async move { async move {
handle_response?; handle_response?;
send?; send?;
rx.await?
let method = T::METHOD;
futures::select! {
response = rx.fuse() => {
let elapsed = started.elapsed();
log::trace!("Took {elapsed:?} to recieve response to {method:?} id {id}");
response?
}
_ = timeout => {
log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}");
anyhow::bail!("LSP request timeout");
}
}
} }
} }