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,82 @@ 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+ let analysis_host = AssertUnwindSafe ( & mut self . analysis_host ) ;
316+ s. spawn ( thread:: ThreadIntent :: LatencySensitive , || {
317+ { analysis_host } . 0 . request_cancellation ( )
318+ } ) ;
319+
320+ // downgrade to read lock to allow more readers while we are normalizing text
321+ let guard = RwLockWriteGuard :: downgrade_to_upgradable ( guard) ;
322+ let vfs: & Vfs = & guard. 0 ;
323+
324+ let mut workspace_structure_change = None ;
325+ // A file was added or deleted
326+ let mut has_structure_changes = false ;
327+ let mut bytes = vec ! [ ] ;
328+ let mut modified_rust_files = vec ! [ ] ;
329+ for file in changed_files. into_values ( ) {
330+ let vfs_path = vfs. file_path ( file. file_id ) ;
331+ if let Some ( ( "rust-analyzer" , Some ( "toml" ) ) ) = vfs_path. name_and_extension ( ) {
332+ // Remember ids to use them after `apply_changes`
333+ modified_ratoml_files. insert ( file. file_id , ( file. kind ( ) , vfs_path. clone ( ) ) ) ;
334+ }
324335
325- if let Some ( path) = vfs_path. as_path ( ) {
326- has_structure_changes |= file. is_created_or_deleted ( ) ;
336+ if let Some ( path) = vfs_path. as_path ( ) {
337+ has_structure_changes |= file. is_created_or_deleted ( ) ;
327338
328- if file. is_modified ( ) && path. extension ( ) == Some ( "rs" ) {
329- modified_rust_files. push ( file. file_id ) ;
330- }
339+ if file. is_modified ( ) && path. extension ( ) == Some ( "rs" ) {
340+ modified_rust_files. push ( file. file_id ) ;
341+ }
331342
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 ) ) ;
343+ let additional_files = self
344+ . config
345+ . discover_workspace_config ( )
346+ . map ( |cfg| {
347+ cfg. files_to_watch . iter ( ) . map ( String :: as_str) . collect :: < Vec < & str > > ( )
348+ } )
349+ . unwrap_or_default ( ) ;
350+
351+ let path = path. to_path_buf ( ) ;
352+ if file. is_created_or_deleted ( ) {
353+ workspace_structure_change. get_or_insert ( ( path, false ) ) . 1 |=
354+ self . crate_graph_file_dependencies . contains ( vfs_path) ;
355+ } else if reload:: should_refresh_for_change (
356+ & path,
357+ file. kind ( ) ,
358+ & additional_files,
359+ ) {
360+ trace ! ( ?path, kind = ?file. kind( ) , "refreshing for a change" ) ;
361+ workspace_structure_change. get_or_insert ( ( path. clone ( ) , false ) ) ;
362+ }
351363 }
352- }
353364
354- // Clear native diagnostics when their file gets deleted
355- if !file. exists ( ) {
356- self . diagnostics . clear_native_for ( file. file_id ) ;
357- }
365+ // Clear native diagnostics when their file gets deleted
366+ if !file. exists ( ) {
367+ self . diagnostics . clear_native_for ( file. file_id ) ;
368+ }
358369
359- let text =
360- if let vfs:: Change :: Create ( v, _) | vfs:: Change :: Modify ( v, _) = file. change {
370+ let text = if let vfs:: Change :: Create ( v, _) | vfs:: Change :: Modify ( v, _) =
371+ file. change
372+ {
361373 String :: from_utf8 ( v) . ok ( ) . map ( |text| {
362374 // FIXME: Consider doing normalization in the `vfs` instead? That allows
363375 // getting rid of some locking
@@ -367,29 +379,28 @@ impl GlobalState {
367379 } else {
368380 None
369381 } ;
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) ;
382+ // delay `line_endings_map` changes until we are done normalizing the text
383+ // this allows delaying the re-acquisition of the write lock
384+ bytes. push ( ( file. file_id , text) ) ;
385+ }
386+ let ( vfs, line_endings_map) = & mut * RwLockUpgradableReadGuard :: upgrade ( guard) ;
387+ bytes. into_iter ( ) . for_each ( |( file_id, text) | {
388+ let text = match text {
389+ None => None ,
390+ Some ( ( text, line_endings) ) => {
391+ line_endings_map. insert ( file_id, line_endings) ;
392+ Some ( text)
393+ }
394+ } ;
395+ change. change_file ( file_id, text) ;
396+ } ) ;
397+ if has_structure_changes {
398+ let roots = self . source_root_config . partition ( vfs) ;
399+ change. set_roots ( roots) ;
400+ }
401+ ( change, modified_rust_files, workspace_structure_change)
384402 } ) ;
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- } ;
391403
392- let _p = span ! ( Level :: INFO , "GlobalState::process_changes/apply_change" ) . entered ( ) ;
393404 self . analysis_host . apply_change ( change) ;
394405 if !modified_ratoml_files. is_empty ( )
395406 || !self . config . same_source_root_parent_map ( & self . local_roots_parent_map )
0 commit comments