@@ -31,6 +31,7 @@ public class ParseCoordinator : IParseCoordinator
31
31
private const int _maxDegreeOfDeclarationResolverParallelism = - 1 ;
32
32
private const int _maxDegreeOfReferenceResolverParallelism = - 1 ;
33
33
private const int _maxDegreeOfModuleStateChangeParallelism = - 1 ;
34
+ private const int _maxReferenceLoadingConcurrency = - 1 ;
34
35
35
36
private readonly IDictionary < IVBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > _componentAttributes
36
37
= new Dictionary < IVBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > ( ) ;
@@ -721,142 +722,188 @@ private string GetReferenceProjectId(IReference reference, IReadOnlyList<IVBProj
721
722
722
723
private void SyncComReferences ( IReadOnlyList < IVBProject > projects , CancellationToken token )
723
724
{
725
+ var unmapped = new ConcurrentBag < IReference > ( ) ;
726
+
727
+ var referencesToLoad = GetReferencesToLoadAndSaveReferencePriority ( projects ) ;
728
+
729
+ State . OnStatusMessageUpdate ( ParserState . LoadingReference . ToString ( ) ) ;
730
+
731
+ var referenceLoadingTaskScheduler = ThrottelingTaskScheduler ( _maxReferenceLoadingConcurrency ) ;
732
+
733
+ //Parallel.ForEach is not used because loading the references can contain IO-bound operations.
724
734
var loadTasks = new List < Task > ( ) ;
725
- var unmapped = new List < IReference > ( ) ;
735
+ foreach ( var reference in referencesToLoad )
736
+ {
737
+ var localReference = reference ;
738
+ loadTasks . Add ( Task . Factory . StartNew (
739
+ ( ) => LoadReference ( localReference , unmapped ) ,
740
+ token ,
741
+ TaskCreationOptions . None ,
742
+ referenceLoadingTaskScheduler
743
+ ) ) ;
744
+ }
745
+
746
+ var notMappedReferences = NonMappedReferences ( projects ) ;
747
+ foreach ( var item in notMappedReferences )
748
+ {
749
+ unmapped . Add ( item ) ;
750
+ }
751
+
752
+ try
753
+ {
754
+ Task . WaitAll ( loadTasks . ToArray ( ) , token ) ;
755
+ }
756
+ catch ( AggregateException exception )
757
+ {
758
+ if ( exception . Flatten ( ) . InnerExceptions . All ( ex => ex is OperationCanceledException ) )
759
+ {
760
+ throw exception . InnerException ; //This eliminates the stack trace, but for the cancellation, this is irrelevant.
761
+ }
762
+ State . SetStatusAndFireStateChanged ( this , ParserState . Error ) ;
763
+ throw ;
764
+ }
765
+ token . ThrowIfCancellationRequested ( ) ;
766
+
767
+ foreach ( var reference in unmapped )
768
+ {
769
+ UnloadComReference ( reference , projects ) ;
770
+ }
771
+ }
772
+
773
+ private List < IReference > GetReferencesToLoadAndSaveReferencePriority ( IReadOnlyList < IVBProject > projects )
774
+ {
775
+ var referencesToLoad = new List < IReference > ( ) ;
726
776
727
777
foreach ( var vbProject in projects )
728
778
{
729
779
var projectId = QualifiedModuleName . GetProjectId ( vbProject ) ;
730
780
var references = vbProject . References ;
781
+
782
+ // use a 'for' loop to store the order of references as a 'priority'.
783
+ // reference resolver needs this to know which declaration to prioritize when a global identifier exists in multiple libraries.
784
+ for ( var priority = 1 ; priority <= references . Count ; priority ++ )
731
785
{
732
- // use a 'for' loop to store the order of references as a 'priority'.
733
- // reference resolver needs this to know which declaration to prioritize when a global identifier exists in multiple libraries.
734
- for ( var priority = 1 ; priority <= references . Count ; priority ++ )
786
+ var reference = references [ priority ] ;
787
+ if ( reference . IsBroken )
735
788
{
736
- var reference = references [ priority ] ;
737
- if ( reference . IsBroken )
738
- {
739
- continue ;
740
- }
741
-
742
- // skip loading Rubberduck.tlb (GUID is defined in AssemblyInfo.cs)
743
- if ( reference . Guid == "{E07C841C-14B4-4890-83E9-8C80B06DD59D}" )
744
- {
745
- // todo: figure out why Rubberduck.tlb *sometimes* throws
746
- //continue;
747
- }
748
- var referencedProjectId = GetReferenceProjectId ( reference , projects ) ;
749
-
750
- ReferencePriorityMap map = null ;
751
- foreach ( var item in _projectReferences )
752
- {
753
- if ( item . ReferencedProjectId == referencedProjectId )
754
- {
755
- map = map != null ? null : item ;
756
- }
757
- }
758
-
759
- if ( map == null )
760
- {
761
- map = new ReferencePriorityMap ( referencedProjectId ) { { projectId , priority } } ;
762
- _projectReferences . Add ( map ) ;
763
- }
764
- else
765
- {
766
- map [ projectId ] = priority ;
767
- }
768
-
769
- if ( ! map . IsLoaded )
770
- {
771
- State . OnStatusMessageUpdate ( ParserState . LoadingReference . ToString ( ) ) ;
772
-
773
- var localReference = reference ;
774
-
775
- loadTasks . Add (
776
- Task . Run ( ( ) =>
777
- {
778
- try
779
- {
780
- Logger . Trace ( string . Format ( "Loading referenced type '{0}'." , localReference . Name ) ) ;
781
-
782
- var comReflector = new ReferencedDeclarationsCollector ( State , localReference , _serializedDeclarationsPath ) ;
783
- if ( comReflector . SerializedVersionExists )
784
- {
785
- Logger . Trace ( string . Format ( "Deserializing reference '{0}'." , localReference . Name ) ) ;
786
- foreach ( var declaration in comReflector . LoadDeclarationsFromXml ( ) )
787
- {
788
- State . AddDeclaration ( declaration ) ;
789
- }
790
- }
791
- else
792
- {
793
- Logger . Trace ( string . Format ( "COM reflecting reference '{0}'." , localReference . Name ) ) ;
794
- foreach ( var declaration in comReflector . LoadDeclarationsFromLibrary ( ) )
795
- {
796
- State . AddDeclaration ( declaration ) ;
797
- }
798
- }
799
- }
800
- catch ( Exception exception )
801
- {
802
- unmapped . Add ( reference ) ;
803
- Logger . Warn ( string . Format ( "Types were not loaded from referenced type library '{0}'." , reference . Name ) ) ;
804
- Logger . Error ( exception ) ;
805
- }
806
- } ) ) ;
807
- map . IsLoaded = true ;
808
- }
789
+ continue ;
790
+ }
791
+
792
+ // skip loading Rubberduck.tlb (GUID is defined in AssemblyInfo.cs)
793
+ if ( reference . Guid == "{E07C841C-14B4-4890-83E9-8C80B06DD59D}" )
794
+ {
795
+ // todo: figure out why Rubberduck.tlb *sometimes* throws
796
+ //continue;
797
+ }
798
+ var referencedProjectId = GetReferenceProjectId ( reference , projects ) ;
799
+
800
+ var map = _projectReferences . FirstOrDefault ( item => item . ReferencedProjectId == referencedProjectId ) ;
801
+
802
+ if ( map == null )
803
+ {
804
+ map = new ReferencePriorityMap ( referencedProjectId ) { { projectId , priority } } ;
805
+ _projectReferences . Add ( map ) ;
806
+ }
807
+ else
808
+ {
809
+ map [ projectId ] = priority ;
810
+ }
811
+
812
+ if ( ! map . IsLoaded )
813
+ {
814
+ referencesToLoad . Add ( reference ) ;
815
+ map . IsLoaded = true ;
809
816
}
810
817
}
811
818
}
819
+ return referencesToLoad ;
820
+ }
812
821
813
- var mappedIds = new List < string > ( ) ;
814
- foreach ( var item in _projectReferences )
822
+ private TaskScheduler ThrottelingTaskScheduler ( int maxLevelOfConcurrency )
823
+ {
824
+ if ( maxLevelOfConcurrency <= 0 )
815
825
{
816
- mappedIds . Add ( item . ReferencedProjectId ) ;
826
+ return TaskScheduler . Default ;
817
827
}
828
+ else
829
+ {
830
+ var taskSchedulerPair = new ConcurrentExclusiveSchedulerPair ( TaskScheduler . Default , maxLevelOfConcurrency ) ;
831
+ return taskSchedulerPair . ConcurrentScheduler ;
832
+ }
833
+ }
818
834
819
- foreach ( var project in projects )
835
+ private void LoadReference ( IReference localReference , ConcurrentBag < IReference > unmapped )
836
+ {
837
+ Logger . Trace ( string . Format ( "Loading referenced type '{0}'." , localReference . Name ) ) ;
838
+ var comReflector = new ReferencedDeclarationsCollector ( State , localReference , _serializedDeclarationsPath ) ;
839
+ try
820
840
{
821
- var references = project . References ;
841
+ if ( comReflector . SerializedVersionExists )
822
842
{
823
- foreach ( var item in references )
824
- {
825
- if ( ! mappedIds . Contains ( GetReferenceProjectId ( item , projects ) ) )
826
- {
827
- unmapped . Add ( item ) ;
828
- }
829
- }
843
+ LoadReferenceByDeserialization ( localReference , comReflector ) ;
844
+ }
845
+ else
846
+ {
847
+ LoadReferenceByCOMReflection ( localReference , comReflector ) ;
830
848
}
831
849
}
850
+ catch ( Exception exception )
851
+ {
852
+ unmapped . Add ( localReference ) ;
853
+ Logger . Warn ( string . Format ( "Types were not loaded from referenced type library '{0}'." , localReference . Name ) ) ;
854
+ Logger . Error ( exception ) ;
855
+ }
856
+ }
832
857
833
- Task . WaitAll ( loadTasks . ToArray ( ) , token ) ;
858
+ private void LoadReferenceByDeserialization ( IReference localReference , ReferencedDeclarationsCollector comReflector )
859
+ {
860
+ Logger . Trace ( string . Format ( "Deserializing reference '{0}'." , localReference . Name ) ) ;
861
+ var declarations = comReflector . LoadDeclarationsFromXml ( ) ;
862
+ foreach ( var declaration in declarations )
863
+ {
864
+ State . AddDeclaration ( declaration ) ;
865
+ }
866
+ }
834
867
835
- foreach ( var reference in unmapped )
868
+ private void LoadReferenceByCOMReflection ( IReference localReference , ReferencedDeclarationsCollector comReflector )
869
+ {
870
+ Logger . Trace ( string . Format ( "COM reflecting reference '{0}'." , localReference . Name ) ) ;
871
+ var declarations = comReflector . LoadDeclarationsFromLibrary ( ) ;
872
+ foreach ( var declaration in declarations )
836
873
{
837
- UnloadComReference ( reference , projects ) ;
874
+ State . AddDeclaration ( declaration ) ;
838
875
}
839
876
}
877
+
878
+ private List < IReference > NonMappedReferences ( IReadOnlyList < IVBProject > projects )
879
+ {
880
+ var mappedIds = _projectReferences . Select ( item => item . ReferencedProjectId ) . ToHashSet ( ) ;
881
+ var references = projects . SelectMany ( project => project . References ) ;
882
+ return references . Where ( item => ! mappedIds . Contains ( GetReferenceProjectId ( item , projects ) ) ) . ToList ( ) ;
883
+ }
840
884
841
885
private void UnloadComReference ( IReference reference , IReadOnlyList < IVBProject > projects )
842
886
{
843
887
var referencedProjectId = GetReferenceProjectId ( reference , projects ) ;
844
888
845
889
ReferencePriorityMap map = null ;
846
- foreach ( var item in _projectReferences )
890
+ try
847
891
{
848
- if ( item . ReferencedProjectId == referencedProjectId )
849
- {
850
- map = map != null ? null : item ;
851
- }
892
+ map = _projectReferences . SingleOrDefault ( item => item . ReferencedProjectId == referencedProjectId ) ;
893
+ }
894
+ catch ( InvalidOperationException exception )
895
+ {
896
+ //There are multiple maps with the same referencedProjectId. That should not happen. (ghost?).
897
+ Logger . Error ( exception , "Failed To unload com reference with referencedProjectID {0} because RD stores multiple instances of it." , referencedProjectId ) ;
898
+ return ;
852
899
}
853
900
854
901
if ( map == null || ! map . IsLoaded )
855
902
{
856
903
// we're removing a reference we weren't tracking? ...this shouldn't happen.
857
- //Debug.Assert(false);
858
904
return ;
859
905
}
906
+
860
907
map . Remove ( referencedProjectId ) ;
861
908
if ( map . Count == 0 )
862
909
{
0 commit comments