Separate timeout and connection dropped errors out (#30457)

This commit is contained in:
Kirill Bulatov 2025-05-10 15:12:58 +03:00 committed by GitHub
parent 39da72161f
commit 471e02d48f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 313 additions and 115 deletions

View file

@ -5,7 +5,12 @@ pub use lsp_types::*;
use anyhow::{Context as _, Result, anyhow};
use collections::HashMap;
use futures::{AsyncRead, AsyncWrite, Future, FutureExt, channel::oneshot, io::BufWriter, select};
use futures::{
AsyncRead, AsyncWrite, Future, FutureExt,
channel::oneshot::{self, Canceled},
io::BufWriter,
select,
};
use gpui::{App, AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};
use notification::DidChangeWorkspaceFolders;
use parking_lot::{Mutex, RwLock};
@ -39,7 +44,7 @@ use std::{
time::{Duration, Instant},
};
use std::{path::Path, process::Stdio};
use util::{ResultExt, TryFutureExt};
use util::{ConnectionResult, ResultExt, TryFutureExt};
const JSON_RPC_VERSION: &str = "2.0";
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
@ -259,7 +264,7 @@ struct Error {
message: String,
}
pub trait LspRequestFuture<O>: Future<Output = O> {
pub trait LspRequestFuture<O>: Future<Output = ConnectionResult<O>> {
fn id(&self) -> i32;
}
@ -284,7 +289,10 @@ impl<F: Future> Future for LspRequest<F> {
}
}
impl<F: Future> LspRequestFuture<F::Output> for LspRequest<F> {
impl<F, O> LspRequestFuture<O> for LspRequest<F>
where
F: Future<Output = ConnectionResult<O>>,
{
fn id(&self) -> i32 {
self.id
}
@ -824,7 +832,17 @@ impl LanguageServer {
cx: &App,
) -> Task<Result<Arc<Self>>> {
cx.spawn(async move |_| {
let response = self.request::<request::Initialize>(params).await?;
let response = self
.request::<request::Initialize>(params)
.await
.into_response()
.with_context(|| {
format!(
"initializing server {}, id {}",
self.name(),
self.server_id()
)
})?;
if let Some(info) = response.server_info {
self.process_name = info.name.into();
}
@ -863,7 +881,13 @@ impl LanguageServer {
select! {
request_result = shutdown_request.fuse() => {
request_result?;
match request_result {
ConnectionResult::Timeout => {
log::warn!("timeout waiting for language server {name} to shutdown");
},
ConnectionResult::ConnectionReset => {},
ConnectionResult::Result(r) => r?,
}
}
_ = timer => {
@ -1084,7 +1108,7 @@ impl LanguageServer {
pub fn request<T: request::Request>(
&self,
params: T::Params,
) -> impl LspRequestFuture<Result<T::Result>> + use<T>
) -> impl LspRequestFuture<T::Result> + use<T>
where
T::Result: 'static + Send,
{
@ -1097,15 +1121,18 @@ impl LanguageServer {
)
}
fn request_internal<T: request::Request>(
fn request_internal<T>(
next_id: &AtomicI32,
response_handlers: &Mutex<Option<HashMap<RequestId, ResponseHandler>>>,
outbound_tx: &channel::Sender<String>,
executor: &BackgroundExecutor,
params: T::Params,
) -> impl LspRequestFuture<Result<T::Result>> + use<T>
) -> impl LspRequestFuture<T::Result> + use<T>
where
T::Result: 'static + Send,
T: request::Request,
// TODO kb
// <T as lsp_types::request::Request>::Result: ConnectionResult,
{
let id = next_id.fetch_add(1, SeqCst);
let message = serde_json::to_string(&Request {
@ -1120,7 +1147,7 @@ impl LanguageServer {
let handle_response = response_handlers
.lock()
.as_mut()
.ok_or_else(|| anyhow!("server shut down"))
.context("server shut down")
.map(|handlers| {
let executor = executor.clone();
handlers.insert(
@ -1153,8 +1180,12 @@ impl LanguageServer {
let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
let started = Instant::now();
LspRequest::new(id, async move {
handle_response?;
send?;
if let Err(e) = handle_response {
return ConnectionResult::Result(Err(e));
}
if let Err(e) = send {
return ConnectionResult::Result(Err(e));
}
let cancel_on_drop = util::defer(move || {
if let Some(outbound_tx) = outbound_tx.upgrade() {
@ -1174,12 +1205,18 @@ impl LanguageServer {
let elapsed = started.elapsed();
log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
cancel_on_drop.abort();
response?
match response {
Ok(response_result) => ConnectionResult::Result(response_result),
Err(Canceled) => {
log::error!("Server reset connection for a request {method:?} id {id}");
ConnectionResult::ConnectionReset
},
}
}
_ = timeout => {
log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}");
anyhow::bail!("LSP request timeout");
ConnectionResult::Timeout
}
}
})
@ -1506,7 +1543,7 @@ impl FakeLanguageServer {
}
/// See [`LanguageServer::request`].
pub async fn request<T>(&self, params: T::Params) -> Result<T::Result>
pub async fn request<T>(&self, params: T::Params) -> ConnectionResult<T::Result>
where
T: request::Request,
T::Result: 'static + Send,
@ -1608,6 +1645,7 @@ impl FakeLanguageServer {
token: NumberOrString::String(token.clone()),
})
.await
.into_response()
.unwrap();
self.notify::<notification::Progress>(&ProgressParams {
token: NumberOrString::String(token),