Skip to content

Commit

Permalink
LS: Refactor DiagnosticsController
Browse files Browse the repository at this point in the history
This commit prepares `DiagnosticsController`
for adding multithreaded processing.

1. `DiagnosticsController::refresh` takes shared reference to owned
   state and makes snapshots internally. It needs it to create `N`
   snapshots for many threads.
2. Encapsulated controller thread state into a struct.
3. `Notifier` and `file_diagnostics` are now stored in controller's
   state instead of being passed around.
4. Some minor renames and doc rewordings.

commit-id:b9bb403e
  • Loading branch information
mkaput committed Nov 14, 2024
1 parent b6aab86 commit 77e2b06
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 36 deletions.
70 changes: 38 additions & 32 deletions crates/cairo-lang-language-server/src/lang/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::lang::diagnostics::file_batches::make_file_batches;
use crate::server::client::Notifier;
use crate::server::panic::cancelled_anyhow;
use crate::server::schedule::thread::{self, JoinHandle, ThreadPriority};
use crate::state::StateSnapshot;
use crate::state::{State, StateSnapshot};

mod file_batches;
mod file_diagnostics;
Expand All @@ -27,42 +27,52 @@ mod trigger;
pub struct DiagnosticsController {
// NOTE: Member order matters here.
// The trigger MUST be dropped before worker's join handle.
// Otherwise, the worker will never be requested to stop, and the worker's JoinHandle will
// never terminate.
trigger: trigger::Sender<WorkerArgs>,
_worker: JoinHandle,
}

struct WorkerArgs {
state: StateSnapshot,
notifier: Notifier,
// Otherwise, the controller thread will never be requested to stop, and the controller's
// JoinHandle will never terminate.
trigger: trigger::Sender<StateSnapshot>,
_thread: JoinHandle,
}

impl DiagnosticsController {
/// Creates a new diagnostics controller.
pub fn new() -> Self {
pub fn new(notifier: Notifier) -> Self {
let (trigger, receiver) = trigger();
let thread = DiagnosticsControllerThread::spawn(receiver, notifier);
Self { trigger, _thread: thread }
}

let worker = thread::Builder::new(ThreadPriority::Worker)
.name("cairo-ls:diagnostics_controller".into())
.spawn(move || Self::control_loop(receiver))
.expect("failed to spawn diagnostics controller thread");

Self { trigger, _worker: worker }
/// Schedules diagnostics refreshing on snapshot(s) of the current state.
pub fn refresh(&self, state: &State) {
self.trigger.activate(state.snapshot());
}
}

/// Schedules diagnostics refreshing using current state snapshot.
pub fn refresh(&self, state: StateSnapshot, notifier: Notifier) {
self.trigger.activate(WorkerArgs { state, notifier });
/// Stores entire state of diagnostics controller's worker thread.
struct DiagnosticsControllerThread {
receiver: trigger::Receiver<StateSnapshot>,
notifier: Notifier,
// NOTE: Globally, we have to always identify files by URL instead of FileId,
// as the diagnostics state is independent of analysis database swaps,
// which invalidate FileIds.
file_diagnostics: HashMap<Url, FileDiagnostics>,
}

impl DiagnosticsControllerThread {
/// Spawns a new diagnostics controller worker thread.
fn spawn(receiver: trigger::Receiver<StateSnapshot>, notifier: Notifier) -> JoinHandle {
let mut this = Self { receiver, notifier, file_diagnostics: Default::default() };

thread::Builder::new(ThreadPriority::Worker)
.name("cairo-ls:diagnostics-controller".into())
.spawn(move || this.event_loop())
.expect("failed to spawn diagnostics controller thread")
}

/// Runs diagnostics controller's event loop.
fn control_loop(receiver: trigger::Receiver<WorkerArgs>) {
let mut file_diagnostics = HashMap::<Url, FileDiagnostics>::new();

while let Some(WorkerArgs { state, notifier }) = receiver.wait() {
fn event_loop(&mut self) {
while let Some(state) = self.receiver.wait() {
if let Err(err) = catch_unwind(AssertUnwindSafe(|| {
Self::control_tick(state, &mut file_diagnostics, notifier);
self.diagnostics_controller_tick(state);
})) {
if let Ok(err) = cancelled_anyhow(err, "diagnostics refreshing has been cancelled")
{
Expand All @@ -76,19 +86,15 @@ impl DiagnosticsController {

/// Runs a single tick of the diagnostics controller's event loop.
#[tracing::instrument(skip_all)]
fn control_tick(
state: StateSnapshot,
file_diagnostics: &mut HashMap<Url, FileDiagnostics>,
notifier: Notifier,
) {
fn diagnostics_controller_tick(&mut self, state: StateSnapshot) {
// TODO(mkaput): Make multiple batches and run them in parallel.
for batch in make_file_batches(&state.db, &state.open_files, NonZero::new(1).unwrap()) {
refresh_diagnostics(
&state.db,
batch,
state.config.trace_macro_diagnostics,
file_diagnostics,
notifier.clone(),
&mut self.file_diagnostics,
self.notifier.clone(),
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/cairo-lang-language-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ impl Backend {
}

/// Calls [`lang::diagnostics::DiagnosticsController::refresh`] to do its work.
fn refresh_diagnostics(state: &mut State, notifier: Notifier) {
state.diagnostics_controller.refresh(state.snapshot(), notifier);
fn refresh_diagnostics(state: &mut State, _notifier: Notifier) {
state.diagnostics_controller.refresh(state);
}

/// Tries to detect the crate root the config that contains a cairo file, and add it to the
Expand Down
4 changes: 2 additions & 2 deletions crates/cairo-lang-language-server/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl State {
tricks: Tricks,
) -> Self {
let notifier = Client::new(sender).notifier();
let scarb_toolchain = ScarbToolchain::new(notifier);
let scarb_toolchain = ScarbToolchain::new(notifier.clone());
let db_swapper = AnalysisDatabaseSwapper::new(scarb_toolchain.clone());

Self {
Expand All @@ -43,7 +43,7 @@ impl State {
scarb_toolchain,
db_swapper,
tricks: Owned::new(tricks.into()),
diagnostics_controller: DiagnosticsController::new(),
diagnostics_controller: DiagnosticsController::new(notifier),
}
}

Expand Down

0 comments on commit 77e2b06

Please sign in to comment.