Skip to content

Commit d908128

Browse files
Merge pull request #74863 from CyrusNajmabadi/batching
2 parents 7087ca7 + 8e54e31 commit d908128

File tree

1 file changed

+47
-174
lines changed

1 file changed

+47
-174
lines changed

src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs

Lines changed: 47 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ private void ChangeProjectProperty<T>(
239239
Func<SolutionChangeAccumulator, ProjectUpdateState, T, ProjectUpdateState> updateSolution,
240240
bool logThrowAwayTelemetry = false)
241241
{
242+
using var _ = CreateBatchScope();
243+
242244
using (_gate.DisposableWait())
243245
{
244246
// If nothing is changing, we can skip entirely
@@ -276,17 +278,8 @@ private void ChangeProjectProperty<T>(
276278
}
277279
}
278280

279-
if (_activeBatchScopes > 0)
280-
{
281-
_projectPropertyModificationsInBatch.Add(
282-
(solutionChanges, projectUpdateState) => updateSolution(solutionChanges, projectUpdateState, oldValue));
283-
}
284-
else
285-
{
286-
_projectSystemProjectFactory.ApplyBatchChangeToWorkspace(
287-
(solutionChanges, projectUpdateState) => updateSolution(solutionChanges, projectUpdateState, oldValue),
288-
onAfterUpdateAlways: null);
289-
}
281+
_projectPropertyModificationsInBatch.Add(
282+
(solutionChanges, projectUpdateState) => updateSolution(solutionChanges, projectUpdateState, oldValue));
290283
}
291284
}
292285

@@ -376,9 +369,9 @@ internal string? CompilationOutputAssemblyFilePath
376369
{
377370
get => _compilationOutputAssemblyFilePath;
378371
set => ChangeProjectOutputPath(
379-
ref _compilationOutputAssemblyFilePath,
380-
value,
381-
s => s.WithProjectCompilationOutputInfo(Id, s.GetRequiredProject(Id).CompilationOutputInfo.WithAssemblyPath(value)));
372+
ref _compilationOutputAssemblyFilePath,
373+
value,
374+
s => s.WithProjectCompilationOutputInfo(Id, s.GetRequiredProject(Id).CompilationOutputInfo.WithAssemblyPath(value)));
382375
}
383376

384377
public string? OutputFilePath
@@ -924,6 +917,8 @@ public void AddAnalyzerReference(string fullPath)
924917

925918
var mappedPaths = GetMappedAnalyzerPaths(fullPath);
926919

920+
using var _ = CreateBatchScope();
921+
927922
using (_gate.DisposableWait())
928923
{
929924
// check all mapped paths first, so that all analyzers are either added or not
@@ -933,43 +928,24 @@ public void AddAnalyzerReference(string fullPath)
933928
throw new ArgumentException($"'{fullPath}' has already been added to this project.", nameof(fullPath));
934929
}
935930

936-
if (_activeBatchScopes > 0)
931+
foreach (var mappedFullPath in mappedPaths)
937932
{
938-
foreach (var mappedFullPath in mappedPaths)
933+
// Are we adding one we just recently removed? If so, we can just keep using that one, and avoid removing
934+
// it once we apply the batch
935+
var analyzerPendingRemoval = _analyzersRemovedInBatch.FirstOrDefault(a => a.FullPath == mappedFullPath);
936+
if (analyzerPendingRemoval != null)
939937
{
940-
// Are we adding one we just recently removed? If so, we can just keep using that one, and avoid removing
941-
// it once we apply the batch
942-
var analyzerPendingRemoval = _analyzersRemovedInBatch.FirstOrDefault(a => a.FullPath == mappedFullPath);
943-
if (analyzerPendingRemoval != null)
944-
{
945-
_analyzersRemovedInBatch.Remove(analyzerPendingRemoval);
946-
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerPendingRemoval);
947-
}
948-
else
949-
{
950-
// Nope, we actually need to make a new one.
951-
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);
952-
953-
_analyzersAddedInBatch.Add(analyzerReference);
954-
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);
955-
}
938+
_analyzersRemovedInBatch.Remove(analyzerPendingRemoval);
939+
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerPendingRemoval);
956940
}
957-
}
958-
else
959-
{
960-
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
941+
else
961942
{
962-
foreach (var mappedFullPath in mappedPaths)
963-
{
964-
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);
965-
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);
966-
w.OnAnalyzerReferenceAdded(Id, analyzerReference);
967-
968-
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferenceAdded(analyzerReference);
969-
}
943+
// Nope, we actually need to make a new one.
944+
var analyzerReference = new AnalyzerFileReference(mappedFullPath, _analyzerAssemblyLoader);
970945

971-
return projectUpdateState;
972-
});
946+
_analyzersAddedInBatch.Add(analyzerReference);
947+
_analyzerPathsToAnalyzers.Add(mappedFullPath, analyzerReference);
948+
}
973949
}
974950
}
975951
}
@@ -981,6 +957,8 @@ public void RemoveAnalyzerReference(string fullPath)
981957

982958
var mappedPaths = GetMappedAnalyzerPaths(fullPath);
983959

960+
using var _ = CreateBatchScope();
961+
984962
using (_gate.DisposableWait())
985963
{
986964
// check all mapped paths first, so that all analyzers are either removed or not
@@ -990,35 +968,16 @@ public void RemoveAnalyzerReference(string fullPath)
990968
throw new ArgumentException($"'{fullPath}' is not an analyzer of this project.", nameof(fullPath));
991969
}
992970

993-
if (_activeBatchScopes > 0)
994-
{
995-
foreach (var mappedFullPath in mappedPaths)
996-
{
997-
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];
998-
999-
_analyzerPathsToAnalyzers.Remove(mappedFullPath);
1000-
1001-
// This analyzer may be one we've just added in the same batch; in that case, just don't add it in
1002-
// the first place.
1003-
if (!_analyzersAddedInBatch.Remove(analyzerReference))
1004-
_analyzersRemovedInBatch.Add(analyzerReference);
1005-
}
1006-
}
1007-
else
971+
foreach (var mappedFullPath in mappedPaths)
1008972
{
1009-
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
1010-
{
1011-
foreach (var mappedFullPath in mappedPaths)
1012-
{
1013-
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];
1014-
_analyzerPathsToAnalyzers.Remove(mappedFullPath);
973+
var analyzerReference = _analyzerPathsToAnalyzers[mappedFullPath];
1015974

1016-
w.OnAnalyzerReferenceRemoved(Id, analyzerReference);
1017-
projectUpdateState = projectUpdateState.WithIncrementalAnalyzerReferenceRemoved(analyzerReference);
1018-
}
975+
_analyzerPathsToAnalyzers.Remove(mappedFullPath);
1019976

1020-
return projectUpdateState;
1021-
});
977+
// This analyzer may be one we've just added in the same batch; in that case, just don't add it in
978+
// the first place.
979+
if (!_analyzersAddedInBatch.Remove(analyzerReference))
980+
_analyzersRemovedInBatch.Add(analyzerReference);
1022981
}
1023982
}
1024983
}
@@ -1080,46 +1039,19 @@ private async ValueTask ProcessFileChangesAsync(ImmutableSegmentedList<string> f
10801039
public void AddMetadataReference(string fullPath, MetadataReferenceProperties properties)
10811040
{
10821041
if (string.IsNullOrEmpty(fullPath))
1083-
{
10841042
throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath));
1085-
}
1043+
1044+
using var _ = CreateBatchScope();
10861045

10871046
using (_gate.DisposableWait())
10881047
{
10891048
if (ContainsMetadataReference_NoLock(fullPath, properties))
1090-
{
10911049
throw new InvalidOperationException("The metadata reference has already been added to the project.");
1092-
}
10931050

10941051
_allMetadataReferences.MultiAdd(fullPath, properties, s_defaultMetadataReferenceProperties);
10951052

1096-
if (_activeBatchScopes > 0)
1097-
{
1098-
if (!_metadataReferencesRemovedInBatch.Remove((fullPath, properties)))
1099-
{
1100-
_metadataReferencesAddedInBatch.Add((fullPath, properties));
1101-
}
1102-
}
1103-
else
1104-
{
1105-
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
1106-
{
1107-
projectUpdateState = ProjectSystemProjectFactory.TryCreateConvertedProjectReference_NoLock(Id, fullPath, properties, projectUpdateState, w.CurrentSolution, out var projectReference);
1108-
1109-
if (projectReference != null)
1110-
{
1111-
w.OnProjectReferenceAdded(Id, projectReference);
1112-
}
1113-
else
1114-
{
1115-
var metadataReference = CreateMetadataReference_NoLock(fullPath, properties, _projectSystemProjectFactory.SolutionServices);
1116-
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceAdded(metadataReference);
1117-
w.OnMetadataReferenceAdded(Id, metadataReference);
1118-
}
1119-
1120-
return projectUpdateState;
1121-
});
1122-
}
1053+
if (!_metadataReferencesRemovedInBatch.Remove((fullPath, properties)))
1054+
_metadataReferencesAddedInBatch.Add((fullPath, properties));
11231055
}
11241056
}
11251057

@@ -1153,50 +1085,19 @@ public ImmutableArray<MetadataReferenceProperties> GetPropertiesForMetadataRefer
11531085
public void RemoveMetadataReference(string fullPath, MetadataReferenceProperties properties)
11541086
{
11551087
if (string.IsNullOrEmpty(fullPath))
1156-
{
11571088
throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath));
1158-
}
1089+
1090+
using var _ = CreateBatchScope();
11591091

11601092
using (_gate.DisposableWait())
11611093
{
11621094
if (!ContainsMetadataReference_NoLock(fullPath, properties))
1163-
{
11641095
throw new InvalidOperationException("The metadata reference does not exist in this project.");
1165-
}
11661096

11671097
_allMetadataReferences.MultiRemove(fullPath, properties);
11681098

1169-
if (_activeBatchScopes > 0)
1170-
{
1171-
if (!_metadataReferencesAddedInBatch.Remove((fullPath, properties)))
1172-
{
1173-
_metadataReferencesRemovedInBatch.Add((fullPath, properties));
1174-
}
1175-
}
1176-
else
1177-
{
1178-
_projectSystemProjectFactory.ApplyChangeToWorkspaceWithProjectUpdateState((w, projectUpdateState) =>
1179-
{
1180-
projectUpdateState = TryRemoveConvertedProjectReference_NoLock(Id, fullPath, properties, projectUpdateState, out var projectReference);
1181-
1182-
// If this was converted to a project reference, we have now recorded the removal -- let's remove it here too
1183-
if (projectReference != null)
1184-
{
1185-
w.OnProjectReferenceRemoved(Id, projectReference);
1186-
}
1187-
else
1188-
{
1189-
// TODO: find a cleaner way to fetch this
1190-
var metadataReference = w.CurrentSolution.GetRequiredProject(Id).MetadataReferences
1191-
.Cast<PortableExecutableReference>()
1192-
.Single(m => m.FilePath == fullPath && m.Properties == properties);
1193-
projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(metadataReference);
1194-
w.OnMetadataReferenceRemoved(Id, metadataReference);
1195-
}
1196-
1197-
return projectUpdateState;
1198-
});
1199-
}
1099+
if (!_metadataReferencesAddedInBatch.Remove((fullPath, properties)))
1100+
_metadataReferencesRemovedInBatch.Add((fullPath, properties));
12001101
}
12011102
}
12021103

@@ -1207,37 +1108,24 @@ public void RemoveMetadataReference(string fullPath, MetadataReferenceProperties
12071108
public void AddProjectReference(ProjectReference projectReference)
12081109
{
12091110
if (projectReference == null)
1210-
{
12111111
throw new ArgumentNullException(nameof(projectReference));
1212-
}
1112+
1113+
using var _ = CreateBatchScope();
12131114

12141115
using (_gate.DisposableWait())
12151116
{
12161117
if (ContainsProjectReference_NoLock(projectReference))
1217-
{
12181118
throw new ArgumentException("The project reference has already been added to the project.");
1219-
}
12201119

1221-
if (_activeBatchScopes > 0)
1222-
{
1223-
if (!_projectReferencesRemovedInBatch.Remove(projectReference))
1224-
{
1225-
_projectReferencesAddedInBatch.Add(projectReference);
1226-
}
1227-
}
1228-
else
1229-
{
1230-
_projectSystemProjectFactory.ApplyChangeToWorkspace(w => w.OnProjectReferenceAdded(Id, projectReference));
1231-
}
1120+
if (!_projectReferencesRemovedInBatch.Remove(projectReference))
1121+
_projectReferencesAddedInBatch.Add(projectReference);
12321122
}
12331123
}
12341124

12351125
public bool ContainsProjectReference(ProjectReference projectReference)
12361126
{
12371127
if (projectReference == null)
1238-
{
12391128
throw new ArgumentNullException(nameof(projectReference));
1240-
}
12411129

12421130
using (_gate.DisposableWait())
12431131
{
@@ -1250,14 +1138,10 @@ private bool ContainsProjectReference_NoLock(ProjectReference projectReference)
12501138
Debug.Assert(_gate.CurrentCount == 0);
12511139

12521140
if (_projectReferencesRemovedInBatch.Contains(projectReference))
1253-
{
12541141
return false;
1255-
}
12561142

12571143
if (_projectReferencesAddedInBatch.Contains(projectReference))
1258-
{
12591144
return true;
1260-
}
12611145

12621146
return _projectSystemProjectFactory.Workspace.CurrentSolution.GetRequiredProject(Id).AllProjectReferences.Contains(projectReference);
12631147
}
@@ -1286,28 +1170,17 @@ public IReadOnlyList<ProjectReference> GetProjectReferences()
12861170
public void RemoveProjectReference(ProjectReference projectReference)
12871171
{
12881172
if (projectReference == null)
1289-
{
12901173
throw new ArgumentNullException(nameof(projectReference));
1291-
}
1174+
1175+
using var _ = CreateBatchScope();
12921176

12931177
using (_gate.DisposableWait())
12941178
{
12951179
if (!ContainsProjectReference_NoLock(projectReference))
1296-
{
12971180
throw new ArgumentException("The project does not contain that project reference.");
1298-
}
12991181

1300-
if (_activeBatchScopes > 0)
1301-
{
1302-
if (!_projectReferencesAddedInBatch.Remove(projectReference))
1303-
{
1304-
_projectReferencesRemovedInBatch.Add(projectReference);
1305-
}
1306-
}
1307-
else
1308-
{
1309-
_projectSystemProjectFactory.ApplyChangeToWorkspace(w => w.OnProjectReferenceRemoved(Id, projectReference));
1310-
}
1182+
if (!_projectReferencesAddedInBatch.Remove(projectReference))
1183+
_projectReferencesRemovedInBatch.Add(projectReference);
13111184
}
13121185
}
13131186

0 commit comments

Comments
 (0)