3
3
//!
4
4
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5
5
6
- use std:: { ops:: Not as _, time:: Instant } ;
6
+ use std:: { ops:: Not as _, panic :: AssertUnwindSafe , time:: Instant } ;
7
7
8
8
use crossbeam_channel:: { Receiver , Sender , unbounded} ;
9
9
use hir:: ChangeWithProcMacros ;
@@ -19,6 +19,7 @@ use parking_lot::{
19
19
use proc_macro_api:: ProcMacroClient ;
20
20
use project_model:: { ManifestPath , ProjectWorkspace , ProjectWorkspaceKind , WorkspaceBuildScripts } ;
21
21
use rustc_hash:: { FxHashMap , FxHashSet } ;
22
+ use stdx:: thread;
22
23
use tracing:: { Level , span, trace} ;
23
24
use triomphe:: Arc ;
24
25
use vfs:: { AbsPathBuf , AnchoredPathBuf , ChangeKind , Vfs , VfsPath } ;
@@ -78,6 +79,7 @@ pub(crate) struct GlobalState {
78
79
79
80
pub ( crate ) task_pool : Handle < TaskPool < Task > , Receiver < Task > > ,
80
81
pub ( crate ) fmt_pool : Handle < TaskPool < Task > , Receiver < Task > > ,
82
+ pub ( crate ) cancellation_pool : thread:: Pool ,
81
83
82
84
pub ( crate ) config : Arc < Config > ,
83
85
pub ( crate ) config_errors : Option < ConfigErrors > ,
@@ -210,6 +212,7 @@ impl GlobalState {
210
212
let handle = TaskPool :: new_with_threads ( sender, 1 ) ;
211
213
Handle { handle, receiver }
212
214
} ;
215
+ let cancellation_pool = thread:: Pool :: new ( 1 ) ;
213
216
214
217
let task_queue = {
215
218
let ( sender, receiver) = unbounded ( ) ;
@@ -230,6 +233,7 @@ impl GlobalState {
230
233
req_queue : ReqQueue :: default ( ) ,
231
234
task_pool,
232
235
fmt_pool,
236
+ cancellation_pool,
233
237
loader,
234
238
config : Arc :: new ( config. clone ( ) ) ,
235
239
analysis_host,
@@ -290,74 +294,82 @@ impl GlobalState {
290
294
291
295
pub ( crate ) fn process_changes ( & mut self ) -> bool {
292
296
let _p = span ! ( Level :: INFO , "GlobalState::process_changes" ) . entered ( ) ;
293
-
294
297
// We cannot directly resolve a change in a ratoml file to a format
295
298
// that can be used by the config module because config talks
296
299
// in `SourceRootId`s instead of `FileId`s and `FileId` -> `SourceRootId`
297
300
// mapping is not ready until `AnalysisHost::apply_changes` has been called.
298
301
let mut modified_ratoml_files: FxHashMap < FileId , ( ChangeKind , vfs:: VfsPath ) > =
299
302
FxHashMap :: default ( ) ;
300
303
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
+ }
308
310
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
+ }
324
335
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 ( ) ;
327
338
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
+ }
331
342
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
+ }
351
363
}
352
- }
353
364
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
+ }
358
369
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
+ {
361
373
String :: from_utf8 ( v) . ok ( ) . map ( |text| {
362
374
// FIXME: Consider doing normalization in the `vfs` instead? That allows
363
375
// getting rid of some locking
@@ -367,29 +379,28 @@ impl GlobalState {
367
379
} else {
368
380
None
369
381
} ;
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)
384
402
} ) ;
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
- } ;
391
403
392
- let _p = span ! ( Level :: INFO , "GlobalState::process_changes/apply_change" ) . entered ( ) ;
393
404
self . analysis_host . apply_change ( change) ;
394
405
if !modified_ratoml_files. is_empty ( )
395
406
|| !self . config . same_source_root_parent_map ( & self . local_roots_parent_map )
0 commit comments