Skip to content

Commit d01b164

Browse files
Merge pull request #73323 from CyrusNajmabadi/parallelHelper
2 parents 26cb20e + 4f00d52 commit d01b164

File tree

9 files changed

+142
-102
lines changed

9 files changed

+142
-102
lines changed

src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -222,23 +222,20 @@ private static async Task FindResultsInUnreferencedProjectSourceSymbolsAsync(
222222

223223
// Defer to the ProducerConsumer. We're search the unreferenced projects in parallel. As we get results, we'll
224224
// add them to the 'allSymbolReferences' queue. If we get enough results, we'll cancel all the other work.
225-
await ProducerConsumer<ImmutableArray<SymbolReference>>.RunAsync(
226-
ProducerConsumerOptions.SingleReaderOptions,
227-
produceItems: static (onItemsFound, args) => RoslynParallel.ForEachAsync(
228-
args.viableUnreferencedProjects,
229-
args.linkedTokenSource.Token,
230-
async (project, cancellationToken) =>
231-
{
232-
// Search in this unreferenced project. But don't search in any of its' direct references. i.e. we
233-
// don't want to search in its metadata references or in the projects it references itself. We'll be
234-
// searching those entities individually.
235-
var references = await args.finder.FindInSourceSymbolsInProjectAsync(
236-
args.projectToAssembly, project, args.exact, cancellationToken).ConfigureAwait(false);
237-
onItemsFound(references);
238-
}),
239-
consumeItems: static (symbolReferencesEnumerable, args) =>
225+
await ProducerConsumer<ImmutableArray<SymbolReference>>.RunParallelAsync(
226+
source: viableUnreferencedProjects,
227+
produceItems: static async (project, onItemsFound, args, cancellationToken) =>
228+
{
229+
// Search in this unreferenced project. But don't search in any of its' direct references. i.e. we
230+
// don't want to search in its metadata references or in the projects it references itself. We'll be
231+
// searching those entities individually.
232+
var references = await args.finder.FindInSourceSymbolsInProjectAsync(
233+
args.projectToAssembly, project, args.exact, cancellationToken).ConfigureAwait(false);
234+
onItemsFound(references);
235+
},
236+
consumeItems: static (symbolReferencesEnumerable, args, cancellationToken) =>
240237
ProcessReferencesAsync(args.allSymbolReferences, args.maxResults, symbolReferencesEnumerable, args.linkedTokenSource),
241-
args: (projectToAssembly, allSymbolReferences, maxResults, finder, exact, viableUnreferencedProjects, linkedTokenSource),
238+
args: (projectToAssembly, allSymbolReferences, maxResults, finder, exact, linkedTokenSource),
242239
linkedTokenSource.Token).ConfigureAwait(false);
243240
}
244241

@@ -266,27 +263,24 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync(
266263

267264
// Defer to the ProducerConsumer. We're search the metadata references in parallel. As we get results, we'll
268265
// add them to the 'allSymbolReferences' queue. If we get enough results, we'll cancel all the other work.
269-
await ProducerConsumer<ImmutableArray<SymbolReference>>.RunAsync(
270-
ProducerConsumerOptions.SingleReaderOptions,
271-
produceItems: static (onItemsFound, args) => RoslynParallel.ForEachAsync(
272-
args.newReferences,
273-
args.linkedTokenSource.Token,
274-
async (tuple, cancellationToken) =>
275-
{
276-
var (referenceProject, reference) = tuple;
277-
var compilation = args.referenceToCompilation.GetOrAdd(
278-
reference, r => CreateCompilation(args.project, r));
279-
280-
// Ignore netmodules. First, they're incredibly esoteric and barely used.
281-
// Second, the SymbolFinder API doesn't even support searching them.
282-
if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol assembly)
283-
return;
284-
285-
var references = await args.finder.FindInMetadataSymbolsAsync(
286-
assembly, referenceProject, reference, args.exact, args.linkedTokenSource.Token).ConfigureAwait(false);
287-
onItemsFound(references);
288-
}),
289-
consumeItems: static (symbolReferencesEnumerable, args) =>
266+
await ProducerConsumer<ImmutableArray<SymbolReference>>.RunParallelAsync(
267+
source: newReferences,
268+
produceItems: static async (tuple, onItemsFound, args, cancellationToken) =>
269+
{
270+
var (referenceProject, reference) = tuple;
271+
var compilation = args.referenceToCompilation.GetOrAdd(
272+
reference, r => CreateCompilation(args.project, r));
273+
274+
// Ignore netmodules. First, they're incredibly esoteric and barely used.
275+
// Second, the SymbolFinder API doesn't even support searching them.
276+
if (compilation.GetAssemblyOrModuleSymbol(reference) is not IAssemblySymbol assembly)
277+
return;
278+
279+
var references = await args.finder.FindInMetadataSymbolsAsync(
280+
assembly, referenceProject, reference, args.exact, cancellationToken).ConfigureAwait(false);
281+
onItemsFound(references);
282+
},
283+
consumeItems: static (symbolReferencesEnumerable, args, cancellationToken) =>
290284
ProcessReferencesAsync(args.allSymbolReferences, args.maxResults, symbolReferencesEnumerable, args.linkedTokenSource),
291285
args: (referenceToCompilation, project, allSymbolReferences, maxResults, finder, exact, newReferences, linkedTokenSource),
292286
linkedTokenSource.Token).ConfigureAwait(false);

src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -121,45 +121,41 @@ public async Task<ImmutableArray<CodeRefactoring>> GetRefactoringsAsync(
121121
foreach (var provider in orderedProviders)
122122
providerToIndex.Add(provider, providerToIndex.Count);
123123

124-
await ProducerConsumer<(CodeRefactoringProvider provider, CodeRefactoring codeRefactoring)>.RunAsync(
125-
ProducerConsumerOptions.SingleReaderOptions,
126-
produceItems: static (callback, args) =>
124+
await ProducerConsumer<(CodeRefactoringProvider provider, CodeRefactoring codeRefactoring)>.RunParallelAsync(
125+
source: orderedProviders,
126+
produceItems: static async (provider, callback, args, cancellationToken) =>
127+
{
127128
// Run all providers in parallel to get the set of refactorings for this document.
128-
RoslynParallel.ForEachAsync(
129-
args.orderedProviders,
130-
args.cancellationToken,
131-
async (provider, cancellationToken) =>
132-
{
133-
// Log an individual telemetry event for slow code refactoring computations to
134-
// allow targeted trace notifications for further investigation. 500 ms seemed like
135-
// a good value so as to not be too noisy, but if fired, indicates a potential
136-
// area requiring investigation.
137-
const int CodeRefactoringTelemetryDelay = 500;
138-
139-
var providerName = provider.GetType().Name;
140-
141-
var logMessage = KeyValueLogMessage.Create(m =>
142-
{
143-
m[TelemetryLogging.KeyName] = providerName;
144-
m[TelemetryLogging.KeyLanguageName] = args.document.Project.Language;
145-
});
146-
147-
using (args.addOperationScope(providerName))
148-
using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken))
149-
using (TelemetryLogging.LogBlockTime(FunctionId.CodeRefactoring_Delay, logMessage, CodeRefactoringTelemetryDelay))
150-
{
151-
var refactoring = await args.@this.GetRefactoringFromProviderAsync(
152-
args.document, args.state, provider, args.options, cancellationToken).ConfigureAwait(false);
153-
if (refactoring != null)
154-
callback((provider, refactoring));
155-
}
156-
}),
157-
consumeItems: static async (reader, args) =>
129+
// Log an individual telemetry event for slow code refactoring computations to
130+
// allow targeted trace notifications for further investigation. 500 ms seemed like
131+
// a good value so as to not be too noisy, but if fired, indicates a potential
132+
// area requiring investigation.
133+
const int CodeRefactoringTelemetryDelay = 500;
134+
135+
var providerName = provider.GetType().Name;
136+
137+
var logMessage = KeyValueLogMessage.Create(m =>
138+
{
139+
m[TelemetryLogging.KeyName] = providerName;
140+
m[TelemetryLogging.KeyLanguageName] = args.document.Project.Language;
141+
});
142+
143+
using (args.addOperationScope(providerName))
144+
using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken))
145+
using (TelemetryLogging.LogBlockTime(FunctionId.CodeRefactoring_Delay, logMessage, CodeRefactoringTelemetryDelay))
146+
{
147+
var refactoring = await args.@this.GetRefactoringFromProviderAsync(
148+
args.document, args.state, provider, args.options, cancellationToken).ConfigureAwait(false);
149+
if (refactoring != null)
150+
callback((provider, refactoring));
151+
}
152+
},
153+
consumeItems: static async (reader, args, cancellationToken) =>
158154
{
159155
await foreach (var pair in reader)
160156
args.pairs.Add(pair);
161157
},
162-
args: (@this: this, document, state, orderedProviders, options, addOperationScope, pairs, cancellationToken),
158+
args: (@this: this, document, state, options, addOperationScope, pairs),
163159
cancellationToken).ConfigureAwait(false);
164160

165161
return pairs

src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ await PerformParallelSearchAsync(
127127

128128
async ValueTask ProcessSingleProjectGroupAsync(
129129
IGrouping<ProjectKey, DocumentKey> group,
130-
Action<RoslynNavigateToItem> onItemFound)
130+
Action<RoslynNavigateToItem> onItemFound,
131+
CancellationToken cancellationToken)
131132
{
132133
if (cancellationToken.IsCancellationRequested)
133134
return;

src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.GeneratedDocumentSearch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public static async Task SearchGeneratedDocumentsInCurrentProcessAsync(
7171
return;
7272

7373
async ValueTask ProcessSingleProjectAsync(
74-
Project project, Action<RoslynNavigateToItem> onItemFound)
74+
Project project, Action<RoslynNavigateToItem> onItemFound, CancellationToken cancellationToken)
7575
{
7676
// First generate all the source-gen docs. Then handoff to the standard search routine to find matches in them.
7777
var sourceGeneratedDocs = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false);

src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.NormalSearch.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ await PerformParallelSearchAsync(
127127

128128
async ValueTask SearchSingleProjectAsync(
129129
Project project,
130-
Action<RoslynNavigateToItem> onItemFound)
130+
Action<RoslynNavigateToItem> onItemFound,
131+
CancellationToken cancellationToken)
131132
{
132133
using var _ = GetPooledHashSet(priorityDocuments.Where(d => project == d.Project), out var highPriDocs);
133134

src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ private static IEnumerable<T> Prioritize<T>(IEnumerable<T> items, Func<T, bool>
9090
/// </summary>
9191
private static Task PerformParallelSearchAsync<T>(
9292
IEnumerable<T> items,
93-
Func<T, Action<RoslynNavigateToItem>, ValueTask> callback,
93+
Func<T, Action<RoslynNavigateToItem>, CancellationToken, ValueTask> callback,
9494
Func<ImmutableArray<RoslynNavigateToItem>, Task> onItemsFound,
9595
CancellationToken cancellationToken)
96-
=> ProducerConsumer<RoslynNavigateToItem>.RunAsync(
97-
ProducerConsumerOptions.SingleReaderOptions,
98-
produceItems: static (onItemFound, args) => RoslynParallel.ForEachAsync(args.items, args.cancellationToken, (item, cancellationToken) => args.callback(item, onItemFound)),
99-
consumeItems: static (items, args) => args.onItemsFound(items),
100-
args: (items, callback, onItemsFound, cancellationToken),
96+
=> ProducerConsumer<RoslynNavigateToItem>.RunParallelAsync(
97+
source: items,
98+
produceItems: static async (item, onItemFound, args, cancellationToken) => await args.callback(item, onItemFound, cancellationToken).ConfigureAwait(false),
99+
consumeItems: static (items, args, cancellationToken) => args.onItemsFound(items),
100+
args: (items, callback, onItemsFound),
101101
cancellationToken);
102102
}

src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ public async Task FindReferencesAsync(
8181
{
8282
await ProducerConsumer<Reference>.RunAsync(
8383
ProducerConsumerOptions.SingleReaderOptions,
84-
produceItems: static (onItemFound, args) => args.@this.PerformSearchAsync(args.symbols, onItemFound, args.cancellationToken),
85-
consumeItems: static async (references, args) => await args.@this._progress.OnReferencesFoundAsync(references, @args.cancellationToken).ConfigureAwait(false),
86-
(@this: this, symbols, cancellationToken),
84+
produceItems: static (onItemFound, args, cancellationToken) => args.@this.PerformSearchAsync(args.symbols, onItemFound, cancellationToken),
85+
consumeItems: static async (references, args, cancellationToken) => await args.@this._progress.OnReferencesFoundAsync(references, cancellationToken).ConfigureAwait(false),
86+
(@this: this, symbols),
8787
cancellationToken).ConfigureAwait(false);
8888
}
8989
finally

0 commit comments

Comments
 (0)