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,83 @@ 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
+ // 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
+ }
324
336
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 ( ) ;
327
339
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
+ }
331
343
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
+ }
351
364
}
352
- }
353
365
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
+ }
358
370
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
+ {
361
374
String :: from_utf8 ( v) . ok ( ) . map ( |text| {
362
375
// FIXME: Consider doing normalization in the `vfs` instead? That allows
363
376
// getting rid of some locking
@@ -367,29 +380,28 @@ impl GlobalState {
367
380
} else {
368
381
None
369
382
} ;
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)
384
403
} ) ;
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
404
392
- let _p = span ! ( Level :: INFO , "GlobalState::process_changes/apply_change" ) . entered ( ) ;
393
405
self . analysis_host . apply_change ( change) ;
394
406
if !modified_ratoml_files. is_empty ( )
395
407
|| !self . config . same_source_root_parent_map ( & self . local_roots_parent_map )
0 commit comments