summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2023-11-30 03:35:39 +0000
committerGitHub <noreply@github.com>2023-11-30 03:35:39 +0000
commit595a2be024b3523197557a8b122e3ce77f1dae3c (patch)
treedc47ae9b95dd56b8f488b3939e493b175bc32e2b
parent6718be87c8468b8ed27548e350c62ad86287a900 (diff)
perf(lsp): cancel ts requests on future drop (#21387)
When an old request is obsoleted while the user is typing, the client will say so to the server and tower-lsp will drop the future associated with that request. This wires that up to the ts server by having any request's token be cancelled when the surrounding state is dropped.
-rw-r--r--cli/lsp/tsc.rs21
1 files changed, 19 insertions, 2 deletions
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index b67b634e1..a907292e0 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -231,8 +231,12 @@ impl TsServer {
start(&mut ts_runtime, false).unwrap();
started = true;
}
- let value = request(&mut ts_runtime, state_snapshot, req, token);
- if tx.send(value).is_err() {
+ let value =
+ request(&mut ts_runtime, state_snapshot, req, token.clone());
+ let was_sent = tx.send(value).is_ok();
+ // Don't print the send error if the token is cancelled, it's expected
+ // to fail in that case and this commonly occurs.
+ if !was_sent && !token.is_cancelled() {
lsp_warn!("Unable to send result to client.");
}
}
@@ -968,11 +972,24 @@ impl TsServer {
where
R: de::DeserializeOwned,
{
+ // When an LSP request is cancelled by the client, the future this is being
+ // executed under and any local variables here will be dropped at the next
+ // await point. To pass on that cancellation to the TS thread, we make this
+ // wrapper which cancels the request's token on drop.
+ struct DroppableToken(CancellationToken);
+ impl Drop for DroppableToken {
+ fn drop(&mut self) {
+ self.0.cancel();
+ }
+ }
+ let token = token.child_token();
+ let droppable_token = DroppableToken(token.clone());
let (tx, rx) = oneshot::channel::<Result<Value, AnyError>>();
if self.sender.send((req, snapshot, tx, token)).is_err() {
return Err(anyhow!("failed to send request to tsc thread"));
}
let value = rx.await??;
+ drop(droppable_token);
Ok(serde_json::from_value::<R>(value)?)
}
}