@@ -182,30 +182,40 @@ impl Repository {
182
182
///
183
183
/// Changes to the index are collected and it's possible to write the index back using [iter::Outcome::write_changes()].
184
184
/// Note that these changes are not observable, they will always be kept.
185
- #[ cfg( feature = "parallel" ) ]
185
+ ///
186
+ /// ### Parallel Operation
187
+ ///
188
+ /// Note that without the `parallel` feature, the iterator becomes 'serial', which means all status will be computed in advance
189
+ /// and it's non-interruptable, yielding worse performance for is-dirty checks for instance as interruptions won't happen.
190
+ /// It's a crutch that is just there to make single-threaded applications possible at all, as it's not really an iterator
191
+ /// anymore. If this matters, better run [Repository::index_worktree_status()] by hand as it provides all control one would need,
192
+ /// just not as an iterator.
186
193
pub struct Iter {
194
+ #[ cfg( feature = "parallel" ) ]
187
195
#[ allow( clippy:: type_complexity) ]
188
196
rx_and_join : Option < (
189
197
std:: sync:: mpsc:: Receiver < iter:: Item > ,
190
198
std:: thread:: JoinHandle < Result < iter:: Outcome , crate :: status:: index_worktree:: Error > > ,
191
199
) > ,
200
+ #[ cfg( feature = "parallel" ) ]
192
201
should_interrupt : std:: sync:: Arc < AtomicBool > ,
202
+ /// Without parallelization, the iterator has to buffer all changes in advance.
203
+ #[ cfg( not( feature = "parallel" ) ) ]
204
+ items : std:: vec:: IntoIter < iter:: Item > ,
193
205
/// The outcome of the operation, only available once the operation has ended.
194
206
out : Option < iter:: Outcome > ,
195
207
/// The set of `(entry_index, change)` we extracted in order to potentially write back the index with the changes applied.
196
208
changes : Vec < ( usize , iter:: ApplyChange ) > ,
197
209
}
198
210
199
211
///
200
- #[ cfg( feature = "parallel" ) ]
201
212
pub mod iter {
202
213
use crate :: bstr:: BString ;
203
214
use crate :: config:: cache:: util:: ApplyLeniencyDefault ;
204
215
use crate :: status:: index_worktree:: iter;
205
216
use crate :: status:: { index_worktree, Platform } ;
206
217
use crate :: worktree:: IndexPersistedOrInMemory ;
207
- use crate :: ThreadSafeRepository ;
208
- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
218
+ use std:: sync:: atomic:: AtomicBool ;
209
219
use std:: sync:: Arc ;
210
220
211
221
pub ( super ) enum ApplyChange {
@@ -434,8 +444,12 @@ pub mod iter {
434
444
#[ error( transparent) ]
435
445
Index ( #[ from] crate :: worktree:: open_index:: Error ) ,
436
446
#[ error( "Failed to spawn producer thread" ) ]
447
+ #[ cfg( feature = "parallel" ) ]
437
448
SpawnThread ( #[ source] std:: io:: Error ) ,
438
449
#[ error( transparent) ]
450
+ #[ cfg( not( feature = "parallel" ) ) ]
451
+ IndexWorktreeStatus ( #[ from] crate :: status:: index_worktree:: Error ) ,
452
+ #[ error( transparent) ]
439
453
ConfigSkipHash ( #[ from] crate :: config:: boolean:: Error ) ,
440
454
#[ error( transparent) ]
441
455
PrepareSubmodules ( #[ from] crate :: submodule:: modules:: Error ) ,
@@ -457,8 +471,6 @@ pub mod iter {
457
471
} ;
458
472
459
473
let should_interrupt = Arc :: new ( AtomicBool :: default ( ) ) ;
460
- let ( tx, rx) = std:: sync:: mpsc:: channel ( ) ;
461
- let mut collect = Collect { tx } ;
462
474
let skip_hash = self
463
475
. repo
464
476
. config
@@ -469,48 +481,93 @@ pub mod iter {
469
481
. with_lenient_default ( self . repo . config . lenient_config ) ?
470
482
. unwrap_or_default ( ) ;
471
483
let submodule = ComputeSubmoduleStatus :: new ( self . repo . clone ( ) . into_sync ( ) , self . submodules ) ?;
472
- let join = std:: thread:: Builder :: new ( )
473
- . name ( "gix::status::index_worktree::iter::producer" . into ( ) )
474
- . spawn ( {
475
- let repo = self . repo . clone ( ) . into_sync ( ) ;
476
- let options = self . index_worktree_options ;
477
- let should_interrupt = should_interrupt. clone ( ) ;
478
- let mut progress = self . progress ;
479
- move || -> Result < _ , crate :: status:: index_worktree:: Error > {
480
- let repo = repo. to_thread_local ( ) ;
481
- let out = repo. index_worktree_status (
482
- & index,
483
- patterns,
484
- & mut collect,
485
- gix_status:: index_as_worktree:: traits:: FastEq ,
486
- submodule,
487
- & mut progress,
488
- & should_interrupt,
489
- options,
490
- ) ?;
491
- Ok ( Outcome {
492
- index_worktree : out,
493
- index,
494
- changes : None ,
495
- skip_hash,
496
- } )
497
- }
484
+ #[ cfg( feature = "parallel" ) ]
485
+ {
486
+ let ( tx, rx) = std:: sync:: mpsc:: channel ( ) ;
487
+ let mut collect = Collect { tx } ;
488
+ let join = std:: thread:: Builder :: new ( )
489
+ . name ( "gix::status::index_worktree::iter::producer" . into ( ) )
490
+ . spawn ( {
491
+ let repo = self . repo . clone ( ) . into_sync ( ) ;
492
+ let options = self . index_worktree_options ;
493
+ let should_interrupt = should_interrupt. clone ( ) ;
494
+ let mut progress = self . progress ;
495
+ move || -> Result < _ , crate :: status:: index_worktree:: Error > {
496
+ let repo = repo. to_thread_local ( ) ;
497
+ let out = repo. index_worktree_status (
498
+ & index,
499
+ patterns,
500
+ & mut collect,
501
+ gix_status:: index_as_worktree:: traits:: FastEq ,
502
+ submodule,
503
+ & mut progress,
504
+ & should_interrupt,
505
+ options,
506
+ ) ?;
507
+ Ok ( Outcome {
508
+ index_worktree : out,
509
+ index,
510
+ changes : None ,
511
+ skip_hash,
512
+ } )
513
+ }
514
+ } )
515
+ . map_err ( Error :: SpawnThread ) ?;
516
+
517
+ Ok ( super :: Iter {
518
+ rx_and_join : Some ( ( rx, join) ) ,
519
+ should_interrupt,
520
+ changes : Vec :: new ( ) ,
521
+ out : None ,
498
522
} )
499
- . map_err ( Error :: SpawnThread ) ?;
500
-
501
- Ok ( super :: Iter {
502
- rx_and_join : Some ( ( rx, join) ) ,
503
- should_interrupt,
504
- changes : Vec :: new ( ) ,
505
- out : None ,
506
- } )
523
+ }
524
+ #[ cfg( not( feature = "parallel" ) ) ]
525
+ {
526
+ let mut collect = Collect { items : Vec :: new ( ) } ;
527
+
528
+ let repo = self . repo . clone ( ) . into_sync ( ) ;
529
+ let options = self . index_worktree_options ;
530
+ let mut progress = self . progress ;
531
+ let repo = repo. to_thread_local ( ) ;
532
+ let out = repo. index_worktree_status (
533
+ & index,
534
+ patterns,
535
+ & mut collect,
536
+ gix_status:: index_as_worktree:: traits:: FastEq ,
537
+ submodule,
538
+ & mut progress,
539
+ & should_interrupt,
540
+ options,
541
+ ) ?;
542
+ let mut out = Outcome {
543
+ index_worktree : out,
544
+ index,
545
+ changes : None ,
546
+ skip_hash,
547
+ } ;
548
+ let mut iter = super :: Iter {
549
+ items : Vec :: new ( ) . into_iter ( ) ,
550
+ changes : Vec :: new ( ) ,
551
+ out : None ,
552
+ } ;
553
+ let items = collect
554
+ . items
555
+ . into_iter ( )
556
+ . filter_map ( |item| iter. maybe_keep_index_change ( item) )
557
+ . collect :: < Vec < _ > > ( ) ;
558
+ out. changes = ( !iter. changes . is_empty ( ) ) . then ( || std:: mem:: take ( & mut iter. changes ) ) ;
559
+ iter. items = items. into_iter ( ) ;
560
+ iter. out = Some ( out) ;
561
+ Ok ( iter)
562
+ }
507
563
}
508
564
}
509
565
510
566
impl Iterator for super :: Iter {
511
567
type Item = Result < Item , crate :: status:: index_worktree:: Error > ;
512
568
513
569
fn next ( & mut self ) -> Option < Self :: Item > {
570
+ #[ cfg( feature = "parallel" ) ]
514
571
loop {
515
572
let ( rx, _join) = self . rx_and_join . as_ref ( ) ?;
516
573
match rx. recv ( ) . ok ( ) {
@@ -523,7 +580,8 @@ pub mod iter {
523
580
None => {
524
581
let ( _rx, handle) = self . rx_and_join . take ( ) ?;
525
582
break match handle. join ( ) . expect ( "no panic" ) {
526
- Ok ( out) => {
583
+ Ok ( mut out) => {
584
+ out. changes = Some ( std:: mem:: take ( & mut self . changes ) ) ;
527
585
self . out = Some ( out) ;
528
586
None
529
587
}
@@ -532,6 +590,15 @@ pub mod iter {
532
590
}
533
591
}
534
592
}
593
+ #[ cfg( not( feature = "parallel" ) ) ]
594
+ self . items . next ( ) . map ( Ok )
595
+ }
596
+ }
597
+
598
+ impl super :: Iter {
599
+ /// Return the outcome of the iteration, or `None` if the iterator isn't fully consumed.
600
+ pub fn outcome_mut ( & mut self ) -> Option < & mut Outcome > {
601
+ self . out . as_mut ( )
535
602
}
536
603
}
537
604
@@ -562,9 +629,10 @@ pub mod iter {
562
629
}
563
630
}
564
631
632
+ #[ cfg( feature = "parallel" ) ]
565
633
impl Drop for super :: Iter {
566
634
fn drop ( & mut self ) {
567
- self . should_interrupt . store ( true , Ordering :: Relaxed ) ;
635
+ self . should_interrupt . store ( true , std :: sync :: atomic :: Ordering :: Relaxed ) ;
568
636
// Allow to temporarily 'leak' the producer to not block on drop, nobody
569
637
// is interested in the result of the thread anymore.
570
638
drop ( self . rx_and_join . take ( ) ) ;
@@ -574,7 +642,10 @@ pub mod iter {
574
642
#[ derive( Clone ) ]
575
643
struct ComputeSubmoduleStatus {
576
644
mode : crate :: status:: Submodule ,
577
- repo : ThreadSafeRepository ,
645
+ #[ cfg( feature = "parallel" ) ]
646
+ repo : crate :: ThreadSafeRepository ,
647
+ #[ cfg( not( feature = "parallel" ) ) ]
648
+ git_dir : std:: path:: PathBuf ,
578
649
submodule_paths : Vec < BString > ,
579
650
}
580
651
@@ -605,7 +676,10 @@ pub mod iter {
605
676
} ;
606
677
Ok ( Self {
607
678
mode,
679
+ #[ cfg( feature = "parallel" ) ]
608
680
repo,
681
+ #[ cfg( not( feature = "parallel" ) ) ]
682
+ git_dir : local_repo. git_dir ( ) . to_owned ( ) ,
609
683
submodule_paths,
610
684
} )
611
685
}
@@ -638,7 +712,12 @@ pub mod iter {
638
712
{
639
713
return Ok ( None ) ;
640
714
}
715
+ #[ cfg( feature = "parallel" ) ]
641
716
let repo = self . repo . to_thread_local ( ) ;
717
+ #[ cfg( not( feature = "parallel" ) ) ]
718
+ let Ok ( repo) = crate :: open ( & self . git_dir ) else {
719
+ return Ok ( None ) ;
720
+ } ;
642
721
let Ok ( Some ( mut submodules) ) = repo. submodules ( ) else {
643
722
return Ok ( None ) ;
644
723
} ;
@@ -656,7 +735,10 @@ pub mod iter {
656
735
}
657
736
658
737
struct Collect {
738
+ #[ cfg( feature = "parallel" ) ]
659
739
tx : std:: sync:: mpsc:: Sender < Item > ,
740
+ #[ cfg( not( feature = "parallel" ) ) ]
741
+ items : Vec < Item > ,
660
742
}
661
743
662
744
impl < ' index > gix_status:: index_as_worktree_with_renames:: VisitEntry < ' index > for Collect {
@@ -673,7 +755,10 @@ pub mod iter {
673
755
> ,
674
756
) {
675
757
// NOTE: we assume that the receiver triggers interruption so the operation will stop if the receiver is down.
758
+ #[ cfg( feature = "parallel" ) ]
676
759
self . tx . send ( entry. into ( ) ) . ok ( ) ;
760
+ #[ cfg( not( feature = "parallel" ) ) ]
761
+ self . items . push ( entry. into ( ) ) ;
677
762
}
678
763
}
679
764
}
0 commit comments