33//!
44//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
55
6- use std:: { ops:: Not as _, time:: Instant } ;
6+ use std:: { ops:: Not as _, panic :: AssertUnwindSafe , time:: Instant } ;
77
88use crossbeam_channel:: { Receiver , Sender , unbounded} ;
99use hir:: ChangeWithProcMacros ;
@@ -19,6 +19,7 @@ use parking_lot::{
1919use proc_macro_api:: ProcMacroClient ;
2020use project_model:: { ManifestPath , ProjectWorkspace , ProjectWorkspaceKind , WorkspaceBuildScripts } ;
2121use rustc_hash:: { FxHashMap , FxHashSet } ;
22+ use stdx:: thread;
2223use tracing:: { Level , span, trace} ;
2324use triomphe:: Arc ;
2425use vfs:: { AbsPathBuf , AnchoredPathBuf , ChangeKind , Vfs , VfsPath } ;
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState {
7879
7980 pub ( crate ) task_pool : Handle < TaskPool < Task > , Receiver < Task > > ,
8081 pub ( crate ) fmt_pool : Handle < TaskPool < Task > , Receiver < Task > > ,
82+ pub ( crate ) cancellation_pool : thread:: Pool ,
8183
8284 pub ( crate ) config : Arc < Config > ,
8385 pub ( crate ) config_errors : Option < ConfigErrors > ,
@@ -210,6 +212,7 @@ impl GlobalState {
210212 let handle = TaskPool :: new_with_threads ( sender, 1 ) ;
211213 Handle { handle, receiver }
212214 } ;
215+ let cancellation_pool = thread:: Pool :: new ( 1 ) ;
213216
214217 let task_queue = {
215218 let ( sender, receiver) = unbounded ( ) ;
@@ -230,6 +233,7 @@ impl GlobalState {
230233 req_queue : ReqQueue :: default ( ) ,
231234 task_pool,
232235 fmt_pool,
236+ cancellation_pool,
233237 loader,
234238 config : Arc :: new ( config. clone ( ) ) ,
235239 analysis_host,
@@ -290,74 +294,83 @@ impl GlobalState {
290294
291295 pub ( crate ) fn process_changes ( & mut self ) -> bool {
292296 let _p = span ! ( Level :: INFO , "GlobalState::process_changes" ) . entered ( ) ;
293-
294297 // We cannot directly resolve a change in a ratoml file to a format
295298 // that can be used by the config module because config talks
296299 // in `SourceRootId`s instead of `FileId`s and `FileId` -> `SourceRootId`
297300 // mapping is not ready until `AnalysisHost::apply_changes` has been called.
298301 let mut modified_ratoml_files: FxHashMap < FileId , ( ChangeKind , vfs:: VfsPath ) > =
299302 FxHashMap :: default ( ) ;
300303
301- let ( change, modified_rust_files, workspace_structure_change) = {
302- let mut change = ChangeWithProcMacros :: default ( ) ;
303- let mut guard = self . vfs . write ( ) ;
304- let changed_files = guard. 0 . take_changes ( ) ;
305- if changed_files. is_empty ( ) {
306- return false ;
307- }
304+ let mut change = ChangeWithProcMacros :: default ( ) ;
305+ let mut guard = self . vfs . write ( ) ;
306+ let changed_files = guard. 0 . take_changes ( ) ;
307+ if changed_files. is_empty ( ) {
308+ return false ;
309+ }
308310
309- // downgrade to read lock to allow more readers while we are normalizing text
310- let guard = RwLockWriteGuard :: downgrade_to_upgradable ( guard) ;
311- let vfs: & Vfs = & guard. 0 ;
312-
313- let mut workspace_structure_change = None ;
314- // A file was added or deleted
315- let mut has_structure_changes = false ;
316- let mut bytes = vec ! [ ] ;
317- let mut modified_rust_files = vec ! [ ] ;
318- for file in changed_files. into_values ( ) {
319- let vfs_path = vfs. file_path ( file. file_id ) ;
320- if let Some ( ( "rust-analyzer" , Some ( "toml" ) ) ) = vfs_path. name_and_extension ( ) {
321- // Remember ids to use them after `apply_changes`
322- modified_ratoml_files. insert ( file. file_id , ( file. kind ( ) , vfs_path. clone ( ) ) ) ;
323- }
311+ let ( change, modified_rust_files, workspace_structure_change) =
312+ self . cancellation_pool . scoped ( |s| {
313+ // start cancellation in parallel, this will kick off lru eviction
314+ // allowing us to do meaningful work while waiting
315+ // FIXME: We should have a long living thread for this purpose instead of re-spawning.
316+ let analysis_host = AssertUnwindSafe ( & mut self . analysis_host ) ;
317+ s. spawn ( thread:: ThreadIntent :: LatencySensitive , || {
318+ { analysis_host } . 0 . request_cancellation ( )
319+ } ) ;
320+
321+ // downgrade to read lock to allow more readers while we are normalizing text
322+ let guard = RwLockWriteGuard :: downgrade_to_upgradable ( guard) ;
323+ let vfs: & Vfs = & guard. 0 ;
324+
325+ let mut workspace_structure_change = None ;
326+ // A file was added or deleted
327+ let mut has_structure_changes = false ;
328+ let mut bytes = vec ! [ ] ;
329+ let mut modified_rust_files = vec ! [ ] ;
330+ for file in changed_files. into_values ( ) {
331+ let vfs_path = vfs. file_path ( file. file_id ) ;
332+ if let Some ( ( "rust-analyzer" , Some ( "toml" ) ) ) = vfs_path. name_and_extension ( ) {
333+ // Remember ids to use them after `apply_changes`
334+ modified_ratoml_files. insert ( file. file_id , ( file. kind ( ) , vfs_path. clone ( ) ) ) ;
335+ }
324336
325- if let Some ( path) = vfs_path. as_path ( ) {
326- has_structure_changes |= file. is_created_or_deleted ( ) ;
337+ if let Some ( path) = vfs_path. as_path ( ) {
338+ has_structure_changes |= file. is_created_or_deleted ( ) ;
327339
328- if file. is_modified ( ) && path. extension ( ) == Some ( "rs" ) {
329- modified_rust_files. push ( file. file_id ) ;
330- }
340+ if file. is_modified ( ) && path. extension ( ) == Some ( "rs" ) {
341+ modified_rust_files. push ( file. file_id ) ;
342+ }
331343
332- let additional_files = self
333- . config
334- . discover_workspace_config ( )
335- . map ( |cfg| {
336- cfg. files_to_watch . iter ( ) . map ( String :: as_str) . collect :: < Vec < & str > > ( )
337- } )
338- . unwrap_or_default ( ) ;
339-
340- let path = path. to_path_buf ( ) ;
341- if file. is_created_or_deleted ( ) {
342- workspace_structure_change. get_or_insert ( ( path, false ) ) . 1 |=
343- self . crate_graph_file_dependencies . contains ( vfs_path) ;
344- } else if reload:: should_refresh_for_change (
345- & path,
346- file. kind ( ) ,
347- & additional_files,
348- ) {
349- trace ! ( ?path, kind = ?file. kind( ) , "refreshing for a change" ) ;
350- workspace_structure_change. get_or_insert ( ( path. clone ( ) , false ) ) ;
344+ let additional_files = self
345+ . config
346+ . discover_workspace_config ( )
347+ . map ( |cfg| {
348+ cfg. files_to_watch . iter ( ) . map ( String :: as_str) . collect :: < Vec < & str > > ( )
349+ } )
350+ . unwrap_or_default ( ) ;
351+
352+ let path = path. to_path_buf ( ) ;
353+ if file. is_created_or_deleted ( ) {
354+ workspace_structure_change. get_or_insert ( ( path, false ) ) . 1 |=
355+ self . crate_graph_file_dependencies . contains ( vfs_path) ;
356+ } else if reload:: should_refresh_for_change (
357+ & path,
358+ file. kind ( ) ,
359+ & additional_files,
360+ ) {
361+ trace ! ( ?path, kind = ?file. kind( ) , "refreshing for a change" ) ;
362+ workspace_structure_change. get_or_insert ( ( path. clone ( ) , false ) ) ;
363+ }
351364 }
352- }
353365
354- // Clear native diagnostics when their file gets deleted
355- if !file. exists ( ) {
356- self . diagnostics . clear_native_for ( file. file_id ) ;
357- }
366+ // Clear native diagnostics when their file gets deleted
367+ if !file. exists ( ) {
368+ self . diagnostics . clear_native_for ( file. file_id ) ;
369+ }
358370
359- let text =
360- if let vfs:: Change :: Create ( v, _) | vfs:: Change :: Modify ( v, _) = file. change {
371+ let text = if let vfs:: Change :: Create ( v, _) | vfs:: Change :: Modify ( v, _) =
372+ file. change
373+ {
361374 String :: from_utf8 ( v) . ok ( ) . map ( |text| {
362375 // FIXME: Consider doing normalization in the `vfs` instead? That allows
363376 // getting rid of some locking
@@ -367,29 +380,28 @@ impl GlobalState {
367380 } else {
368381 None
369382 } ;
370- // delay `line_endings_map` changes until we are done normalizing the text
371- // this allows delaying the re-acquisition of the write lock
372- bytes. push ( ( file. file_id , text) ) ;
373- }
374- let ( vfs, line_endings_map) = & mut * RwLockUpgradableReadGuard :: upgrade ( guard) ;
375- bytes. into_iter ( ) . for_each ( |( file_id, text) | {
376- let text = match text {
377- None => None ,
378- Some ( ( text, line_endings) ) => {
379- line_endings_map. insert ( file_id, line_endings) ;
380- Some ( text)
381- }
382- } ;
383- change. change_file ( file_id, text) ;
383+ // delay `line_endings_map` changes until we are done normalizing the text
384+ // this allows delaying the re-acquisition of the write lock
385+ bytes. push ( ( file. file_id , text) ) ;
386+ }
387+ let ( vfs, line_endings_map) = & mut * RwLockUpgradableReadGuard :: upgrade ( guard) ;
388+ bytes. into_iter ( ) . for_each ( |( file_id, text) | {
389+ let text = match text {
390+ None => None ,
391+ Some ( ( text, line_endings) ) => {
392+ line_endings_map. insert ( file_id, line_endings) ;
393+ Some ( text)
394+ }
395+ } ;
396+ change. change_file ( file_id, text) ;
397+ } ) ;
398+ if has_structure_changes {
399+ let roots = self . source_root_config . partition ( vfs) ;
400+ change. set_roots ( roots) ;
401+ }
402+ ( change, modified_rust_files, workspace_structure_change)
384403 } ) ;
385- if has_structure_changes {
386- let roots = self . source_root_config . partition ( vfs) ;
387- change. set_roots ( roots) ;
388- }
389- ( change, modified_rust_files, workspace_structure_change)
390- } ;
391404
392- let _p = span ! ( Level :: INFO , "GlobalState::process_changes/apply_change" ) . entered ( ) ;
393405 self . analysis_host . apply_change ( change) ;
394406 if !modified_ratoml_files. is_empty ( )
395407 || !self . config . same_source_root_parent_map ( & self . local_roots_parent_map )
0 commit comments