diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 5056ddee461..e067c7f6408 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -1098,6 +1098,28 @@ module CancellableTasks = return! Task.WhenAll (tasks) } + /// Runs the given tasks concurrently, but caps the number of tasks that are running at + /// the same time to at most maxDegreeOfParallelism, to avoid launching an unbounded number + /// of parallel typechecks (e.g. one per document/project) at once. + let inline whenAllThrottled maxDegreeOfParallelism (tasks: CancellableTask<'a> seq) = + cancellableTask { + let! ct = getCancellationToken () + use semaphore = new SemaphoreSlim(maxDegreeOfParallelism: int) + + let runThrottled (task: CancellableTask<'a>) = + backgroundTask { + do! semaphore.WaitAsync(ct) + + try + return! start ct task + finally + semaphore.Release() |> ignore + } + + let tasks = seq { for task in tasks do yield runThrottled task } + return! Task.WhenAll (tasks) + } + let inline whenAllTasks (tasks: CancellableTask seq) = cancellableTask { let! ct = getCancellationToken () diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index c7eb1e50e41..a9825587e94 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -1,4 +1,4 @@ -[] +[] module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System @@ -707,7 +707,8 @@ type Project with documents |> Seq.map (fun doc -> doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (fun range -> onFound doc range), userOpName)) - |> CancellableTask.whenAll + // Throttle to avoid launching a typecheck per document in the project all at once. + |> CancellableTask.whenAllThrottled (max 1 Environment.ProcessorCount) else for doc in documents do do! doc.FindFSharpReferencesAsync(symbol, projectSnapshot, (onFound doc), userOpName)