Move main LSP loop off Tokio to an owned thread#1277
Conversation
| /// The returned [`LoopHandles`] owns everything the loops need. Drop it to | ||
| /// shut the loops down and release the owned state. | ||
| pub(crate) fn start(self) -> LoopHandles { | ||
| let mut aux = tokio::task::JoinSet::<()>::new(); |
There was a problem hiding this comment.
Do you really need a tokio::task::JoinSet anymore for 1 task?
There was a problem hiding this comment.
That's a nice way of cancelling on abort. Dropping a task handle doesn't cancel it, so we'd need a custom Drop method to call the handle's abort() method.
There was a problem hiding this comment.
Dropping a task handle doesn't cancel it
oh i was not aware of that
| /// Pull the next event off the channel. Only the test pump uses this; the | ||
| /// running loop selects on the channel directly so it can also watch for | ||
| /// shutdown. | ||
| #[cfg(test)] |
There was a problem hiding this comment.
i would just remove this
There was a problem hiding this comment.
Let's keep it as a token of the nicer loop structure
| // handlers that mutate those still refresh explicitly. | ||
| let old_revision = salsa::plumbing::current_revision(&self.world.db); | ||
|
|
||
| lsp::log_info!("Processing next event"); |
There was a problem hiding this comment.
This one doesn't feel that useful? We log at the beginning of every event type
| lsp::log_info!( | ||
| "Entering notification handler with {n} outstanding Salsa db holds", | ||
| n = self.world.db.outstanding_holds() | ||
| ); |
There was a problem hiding this comment.
Why do we want to know this on every iteration? Why do we want to know this at all? It feels like itll be expected for the db to have outstanding holds in background threads (i.e. its working on diagnostics when we get a did-change update)
There was a problem hiding this comment.
This is not every iteration, this is only when we're about to write to the DB.
This is meant to help debugging hangs from user logs, we can remove this later once proven stable.
| loop { | ||
| let Some(task) = rx.recv().await else { | ||
| lsp::log_warn!("process_diagnostics_queue: channel closed, task exiting"); | ||
| return; | ||
| }; |
There was a problem hiding this comment.
I feel like the previous while look was pretty nice?
You'd just put the lsp::log_warn!("process_diagnostics_queue: channel closed, task exiting"); part after the while loop exits, i think?
| .map(|task| (task.file.wire_url.clone(), task)) | ||
| .collect(); | ||
|
|
||
| tracing::trace!("Processing {n} diagnostic tasks", n = batch.len()); |
There was a problem hiding this comment.
Since we don't say Generating diagnostics or Finished diagnostics via tracing::trace! (we only do that via lsp::log_info!), i feel like this isn't actually that useful on the kernel side (i know its not new from you, but maybe we can just remove it)
How do we decide where to log?
There was a problem hiding this comment.
I decided to keep it there because diagnostics code logs at kernel level, so we get a section marker.
In general, the current split is that eventing and dispatch belong to the LSP, handling belongs to the kernel. This is a remnant from r_task()-heavy handling and we have nicer logging facilities on the kernel side (context, profiling).
But: when the LSP becomes standalone, everything moves to the LSP.
7979c1d to
c9eb298
Compare
2816d7d to
9160639
Compare
c9eb298 to
5c53c2f
Compare
45ef459 to
dcc5514
Compare
Branched from #1274
Progress towards #1212
Fixes a source of deadlocks when the main loop blocks on a Salsa write until it gains exclusive access, but background tasks hold a DB handle and are scheduled by Tokio to run on the same worker thread as the main loop. We could wrap writes in
block_in_place()instead, which instructs Tokio to move all pending tasks to a different worker, but moving the main loop off tokio altogether is really the safer move.From the documenting comment:
Fixes a bunch of issues observed in frontend tests: https://github.com/posit-dev/positron/actions/runs/27755066106?pr=14254
We still use tokio for the auxiliary loop and the blocking worker pool, for the time being.