Skip to content

Commit 52bb94d

Browse files
committed
internal: Give rustfmt jobs a separate thread
1 parent b7497fc commit 52bb94d

File tree

4 files changed

+54
-29
lines changed

4 files changed

+54
-29
lines changed

crates/rust-analyzer/src/dispatch.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<'a> RequestDispatcher<'a> {
135135
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
136136
R::Result: Serialize,
137137
{
138-
self.on_with_thread_intent::<R>(ThreadIntent::Worker, f)
138+
self.on_with_thread_intent::<true, R>(ThreadIntent::Worker, f)
139139
}
140140

141141
/// Dispatches a latency-sensitive request onto the thread pool.
@@ -148,7 +148,22 @@ impl<'a> RequestDispatcher<'a> {
148148
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
149149
R::Result: Serialize,
150150
{
151-
self.on_with_thread_intent::<R>(ThreadIntent::LatencySensitive, f)
151+
self.on_with_thread_intent::<true, R>(ThreadIntent::LatencySensitive, f)
152+
}
153+
154+
/// Formatting requests should never block on waiting a for task thread to open up, editors will wait
155+
/// on the response and a late formatting update might mess with the document and user.
156+
/// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete!
157+
pub(crate) fn on_fmt_thread<R>(
158+
&mut self,
159+
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
160+
) -> &mut Self
161+
where
162+
R: lsp_types::request::Request + 'static,
163+
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
164+
R::Result: Serialize,
165+
{
166+
self.on_with_thread_intent::<false, R>(ThreadIntent::LatencySensitive, f)
152167
}
153168

154169
pub(crate) fn finish(&mut self) {
@@ -163,7 +178,7 @@ impl<'a> RequestDispatcher<'a> {
163178
}
164179
}
165180

166-
fn on_with_thread_intent<R>(
181+
fn on_with_thread_intent<const MAIN_POOL: bool, R>(
167182
&mut self,
168183
intent: ThreadIntent,
169184
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -178,17 +193,20 @@ impl<'a> RequestDispatcher<'a> {
178193
None => return self,
179194
};
180195

181-
self.global_state.task_pool.handle.spawn(intent, {
182-
let world = self.global_state.snapshot();
183-
move || {
184-
let result = panic::catch_unwind(move || {
185-
let _pctx = stdx::panic_context::enter(panic_context);
186-
f(world, params)
187-
});
188-
match thread_result_to_response::<R>(req.id.clone(), result) {
189-
Ok(response) => Task::Response(response),
190-
Err(_) => Task::Retry(req),
191-
}
196+
let world = self.global_state.snapshot();
197+
if MAIN_POOL {
198+
&mut self.global_state.task_pool.handle
199+
} else {
200+
&mut self.global_state.fmt_pool.handle
201+
}
202+
.spawn(intent, move || {
203+
let result = panic::catch_unwind(move || {
204+
let _pctx = stdx::panic_context::enter(panic_context);
205+
f(world, params)
206+
});
207+
match thread_result_to_response::<R>(req.id.clone(), result) {
208+
Ok(response) => Task::Response(response),
209+
Err(_) => Task::Retry(req),
192210
}
193211
});
194212

crates/rust-analyzer/src/global_state.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub(crate) struct GlobalState {
5454
req_queue: ReqQueue,
5555

5656
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
57+
pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
5758

5859
pub(crate) config: Arc<Config>,
5960
pub(crate) config_errors: Option<ConfigError>,
@@ -151,6 +152,11 @@ impl GlobalState {
151152
let handle = TaskPool::new_with_threads(sender, config.main_loop_num_threads());
152153
Handle { handle, receiver }
153154
};
155+
let fmt_pool = {
156+
let (sender, receiver) = unbounded();
157+
let handle = TaskPool::new_with_threads(sender, 1);
158+
Handle { handle, receiver }
159+
};
154160

155161
let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
156162
if let Some(capacities) = config.lru_query_capacities() {
@@ -161,6 +167,7 @@ impl GlobalState {
161167
sender,
162168
req_queue: ReqQueue::default(),
163169
task_pool,
170+
fmt_pool,
164171
loader,
165172
config: Arc::new(config.clone()),
166173
analysis_host,

crates/rust-analyzer/src/handlers/request.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ use lsp_server::ErrorCode;
1818
use lsp_types::{
1919
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
2020
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
21-
CodeLens, CompletionItem, DocumentFormattingParams, FoldingRange, FoldingRangeParams,
22-
HoverContents, InlayHint, InlayHintParams, Location, LocationLink, Position,
23-
PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
24-
SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
25-
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
26-
TextDocumentIdentifier, Url, WorkspaceEdit,
21+
CodeLens, CompletionItem, FoldingRange, FoldingRangeParams, HoverContents, InlayHint,
22+
InlayHintParams, Location, LocationLink, Position, PrepareRenameResponse, Range, RenameParams,
23+
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
24+
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
25+
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
2726
};
2827
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
2928
use serde_json::json;
@@ -1077,7 +1076,7 @@ pub(crate) fn handle_references(
10771076

10781077
pub(crate) fn handle_formatting(
10791078
snap: GlobalStateSnapshot,
1080-
params: DocumentFormattingParams,
1079+
params: lsp_types::DocumentFormattingParams,
10811080
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
10821081
let _p = profile::span("handle_formatting");
10831082

crates/rust-analyzer/src/main_loop.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ impl GlobalState {
175175
msg.ok().map(Event::Lsp),
176176

177177
recv(self.task_pool.receiver) -> task =>
178+
Some(Event::Task(task.unwrap())),
179+
180+
recv(self.fmt_pool.receiver) -> task =>
178181
Some(Event::Task(task.unwrap())),
179182

180183
recv(self.loader.receiver) -> task =>
@@ -678,6 +681,12 @@ impl GlobalState {
678681
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
679682
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
680683
.on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
684+
// Formatting should be done immediately as the editor might wait on it, but we can't
685+
// put it on the main thread as we do not want the main thread to block on rustfmt.
686+
// So we have an extra thread just for formatting requests to make sure it gets handled
687+
// as fast as possible.
688+
.on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
689+
.on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
681690
// We can’t run latency-sensitive request handlers which do semantic
682691
// analysis on the main thread because that would block other
683692
// requests. Instead, we run these request handlers on higher priority
@@ -695,14 +704,6 @@ impl GlobalState {
695704
.on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
696705
handlers::handle_semantic_tokens_range,
697706
)
698-
// Formatting is not caused by the user typing,
699-
// but it does qualify as latency-sensitive
700-
// because a delay before formatting is applied
701-
// can be confusing for the user.
702-
.on_latency_sensitive::<lsp_types::request::Formatting>(handlers::handle_formatting)
703-
.on_latency_sensitive::<lsp_types::request::RangeFormatting>(
704-
handlers::handle_range_formatting,
705-
)
706707
// All other request handlers
707708
.on::<lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list)
708709
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)

0 commit comments

Comments
 (0)