diff --git a/src/EditorFeatures/Core.Wpf/Options/LegacyEditorConfigDocumentOptionsProviderFactory.cs b/src/EditorFeatures/Core.Wpf/Options/LegacyEditorConfigDocumentOptionsProviderFactory.cs
index f31f5c060938d40ded99420b9761b796ab92f07c..1fdd2772779e5ed7ef5135b36681be0ae4838efe 100644
--- a/src/EditorFeatures/Core.Wpf/Options/LegacyEditorConfigDocumentOptionsProviderFactory.cs
+++ b/src/EditorFeatures/Core.Wpf/Options/LegacyEditorConfigDocumentOptionsProviderFactory.cs
@@ -7,6 +7,7 @@
using System;
using System.Composition;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
@@ -70,18 +71,17 @@ class LegacyEditorConfigDocumentOptionsProviderFactory : IDocumentOptionsProvide
/// An implementation of that ensures we don't watch for a file synchronously to
/// avoid deadlocks.
///
- internal class DeferredFileWatcher : IFileWatcher
+ internal sealed class DeferredFileWatcher : IFileWatcher
{
private readonly IFileWatcher _fileWatcher;
- private readonly SimpleTaskQueue _taskQueue = new SimpleTaskQueue(TaskScheduler.Default);
- private readonly IAsynchronousOperationListener _listener;
+ private readonly TaskQueue _taskQueue;
public DeferredFileWatcher(IFileWatcher fileWatcher, IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider)
{
_fileWatcher = fileWatcher;
_fileWatcher.ConventionFileChanged += OnConventionFileChangedAsync;
- _listener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Workspace);
+ _taskQueue = new TaskQueue(asynchronousOperationListenerProvider.GetListener(FeatureAttribute.Workspace), TaskScheduler.Default);
}
private Task OnConventionFileChangedAsync(object sender, ConventionsFileChangeEventArgs arg)
@@ -112,12 +112,10 @@ public void Dispose()
public void StartWatching(string fileName, string directoryPath)
{
- var asyncToken = _listener.BeginAsyncOperation(nameof(DeferredFileWatcher) + "." + nameof(StartWatching));
-
// Read the file time stamp right now; we want to know if it changes between now
// and our ability to get the file watcher in place.
var originalFileTimeStamp = TryGetFileTimeStamp(fileName, directoryPath);
- _taskQueue.ScheduleTask(() =>
+ _taskQueue.ScheduleTask(nameof(DeferredFileWatcher) + "." + nameof(StartWatching), () =>
{
_fileWatcher.StartWatching(fileName, directoryPath);
@@ -143,7 +141,7 @@ public void StartWatching(string fileName, string directoryPath)
ConventionFileChanged?.Invoke(this,
new ConventionsFileChangeEventArgs(fileName, directoryPath, changeType));
}
- }).CompletesAsyncOperation(asyncToken);
+ }, CancellationToken.None);
}
private static DateTime? TryGetFileTimeStamp(string fileName, string directoryPath)
@@ -168,7 +166,9 @@ public void StartWatching(string fileName, string directoryPath)
public void StopWatching(string fileName, string directoryPath)
{
- _taskQueue.ScheduleTask(() => _fileWatcher.StopWatching(fileName, directoryPath));
+ _taskQueue.ScheduleTask(nameof(DeferredFileWatcher) + "." + nameof(StopWatching),
+ () => _fileWatcher.StopWatching(fileName, directoryPath),
+ CancellationToken.None);
}
}
}
diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorTaskSchedulerFactory.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorTaskSchedulerFactory.cs
deleted file mode 100644
index d4374aa6f231096de4ef5aaf6088bd91c52190bb..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorTaskSchedulerFactory.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Collections.Generic;
-using System.Composition;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Host;
-using Microsoft.CodeAnalysis.Host.Mef;
-using Microsoft.CodeAnalysis.Shared.TestHooks;
-
-namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces
-{
- [ExportWorkspaceService(typeof(IWorkspaceTaskSchedulerFactory), ServiceLayer.Editor), Shared]
- internal class EditorTaskSchedulerFactory : WorkspaceTaskSchedulerFactory
- {
- private readonly IAsynchronousOperationListener _listener;
-
- [ImportingConstructor]
- public EditorTaskSchedulerFactory(IAsynchronousOperationListenerProvider listenerProvider)
- {
- _listener = listenerProvider.GetListener(FeatureAttribute.Workspace);
- }
-
- protected override object BeginAsyncOperation(string taskName)
- {
- return _listener.BeginAsyncOperation(taskName);
- }
-
- protected override void CompleteAsyncOperation(object asyncToken, Task task)
- {
- task.CompletesAsyncOperation((IAsyncToken)asyncToken);
- }
- }
-}
diff --git a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs
index 654f3adba42fc0b6735b032182f41edb013f1ae5..55e6854ea4a5d9bed2db6cc36cd154c682cf1ee8 100644
--- a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs
+++ b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs
@@ -5,11 +5,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.Providers;
+using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
@@ -251,11 +254,24 @@ protected internal override HostWorkspaceServices CreateWorkspaceServices(Worksp
}
}
+ private sealed class MockTaskSchedulerProvider : ITaskSchedulerProvider
+ {
+ public TaskScheduler CurrentContextScheduler
+ => (SynchronizationContext.Current != null) ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default;
+ }
+
+ private sealed class MockWorkspaceAsynchronousOperationListenerProvider : IWorkspaceAsynchronousOperationListenerProvider
+ {
+ public IAsynchronousOperationListener GetListener()
+ => AsynchronousOperationListenerProvider.NullListener;
+ }
+
private class MockHostWorkspaceServices : HostWorkspaceServices
{
private readonly HostServices _hostServices;
private readonly Workspace _workspace;
- private static readonly IWorkspaceTaskSchedulerFactory s_taskSchedulerFactory = new WorkspaceTaskSchedulerFactory();
+ private static readonly ITaskSchedulerProvider s_taskSchedulerProvider = new MockTaskSchedulerProvider();
+ private static readonly IWorkspaceAsynchronousOperationListenerProvider s_asyncListenerProvider = new MockWorkspaceAsynchronousOperationListenerProvider();
private readonly OptionServiceFactory.OptionService _optionService;
public MockHostWorkspaceServices(HostServices hostServices, Workspace workspace)
@@ -278,11 +294,17 @@ public override IEnumerable FindLanguageServices()
{
- if (s_taskSchedulerFactory is TWorkspaceService)
+ if (s_taskSchedulerProvider is TWorkspaceService)
{
- return (TWorkspaceService)s_taskSchedulerFactory;
+ return (TWorkspaceService)s_taskSchedulerProvider;
}
- else if (_optionService is TWorkspaceService workspaceOptionService)
+
+ if (s_asyncListenerProvider is TWorkspaceService)
+ {
+ return (TWorkspaceService)s_asyncListenerProvider;
+ }
+
+ if (_optionService is TWorkspaceService workspaceOptionService)
{
return workspaceOptionService;
}
diff --git a/src/EditorFeatures/TestUtilities/MinimalTestExportProvider.cs b/src/EditorFeatures/TestUtilities/MinimalTestExportProvider.cs
index 6aa1edf8ffb18f93381a8e549677713cd1c401f4..ec122043d2f77d00a18f4c6134d196029da31b79 100644
--- a/src/EditorFeatures/TestUtilities/MinimalTestExportProvider.cs
+++ b/src/EditorFeatures/TestUtilities/MinimalTestExportProvider.cs
@@ -20,8 +20,7 @@ public static Type[] GetLanguageNeutralTypes()
var types = new[]
{
// ROSLYN
- typeof(Microsoft.CodeAnalysis.Editor.Implementation.Workspaces.EditorTaskSchedulerFactory),
- typeof(Microsoft.CodeAnalysis.Host.WorkspaceTaskSchedulerFactory),
+ typeof(Microsoft.CodeAnalysis.Host.TaskSchedulerProvider),
typeof(Microsoft.CodeAnalysis.Formatting.Rules.DefaultFormattingRuleFactoryServiceFactory),
typeof(Microsoft.CodeAnalysis.Host.PersistentStorageServiceFactory),
typeof(Microsoft.CodeAnalysis.Text.Implementation.TextBufferFactoryService.TextBufferCloneServiceFactory),
diff --git a/src/EditorFeatures/TestUtilities/TestExportProvider.cs b/src/EditorFeatures/TestUtilities/TestExportProvider.cs
index f722dd92e1edc9a62e9128264242660fad397921..b8e6bdb6d022240b5586b0952d6b2d422732a6ab 100644
--- a/src/EditorFeatures/TestUtilities/TestExportProvider.cs
+++ b/src/EditorFeatures/TestUtilities/TestExportProvider.cs
@@ -93,6 +93,7 @@ private static Type[] GetNeutralAndCSharpAndVisualBasicTypes()
typeof(CodeAnalysis.Execution.DesktopReferenceSerializationServiceFactory),
typeof(CodeAnalysis.Execution.SerializerServiceFactory),
typeof(CodeAnalysis.Shared.TestHooks.AsynchronousOperationListenerProvider),
+ typeof(CodeAnalysis.Host.WorkspaceAsynchronousOperationListenerProvider),
typeof(PrimaryWorkspace),
typeof(TestExportProvider),
typeof(ThreadingContext),
diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestForegroundNotificationService.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestForegroundNotificationService.cs
index 37a6e7f44e6ad86f503b3f6803b9a4cfc8ff064c..d55c8f8eafeecfbb100dcc58fd787a4b3ae4ef77 100644
--- a/src/EditorFeatures/TestUtilities/Workspaces/TestForegroundNotificationService.cs
+++ b/src/EditorFeatures/TestUtilities/Workspaces/TestForegroundNotificationService.cs
@@ -15,19 +15,20 @@ internal class TestForegroundNotificationService : IForegroundNotificationServic
{
private readonly object _gate = new object();
private readonly List _tasks = new List();
- private readonly SimpleTaskQueue _queue = new SimpleTaskQueue(TaskScheduler.Default);
+ private readonly TaskQueue _queue = new TaskQueue(AsynchronousOperationListenerProvider.NullListener, TaskScheduler.Default);
public void RegisterNotification(Func action, IAsyncToken asyncToken, CancellationToken cancellationToken = default)
{
RegisterNotification(action, 0, asyncToken, cancellationToken);
}
+#pragma warning disable CS0618 // Type or member is obsolete (ScheduleTaskInProgress: https://github.com/dotnet/roslyn/issues/42742)
public void RegisterNotification(Func action, int delayInMS, IAsyncToken asyncToken, CancellationToken cancellationToken = default)
{
Task task;
lock (_gate)
{
- task = _queue.ScheduleTask(() => Execute_NoLock(action, asyncToken, cancellationToken), cancellationToken);
+ task = _queue.ScheduleTaskInProgress(() => Execute_NoLock(action, asyncToken, cancellationToken), cancellationToken);
_tasks.Add(task);
}
@@ -42,7 +43,7 @@ private void Execute_NoLock(Func action, IAsyncToken asyncToken, Cancellat
}
else
{
- _tasks.Add(_queue.ScheduleTask(() => Execute_NoLock(action, asyncToken, cancellationToken), cancellationToken));
+ _tasks.Add(_queue.ScheduleTaskInProgress(() => Execute_NoLock(action, asyncToken, cancellationToken), cancellationToken));
}
}
@@ -56,15 +57,13 @@ public void RegisterNotification(Action action, int delayInMS, IAsyncToken async
Task task;
lock (_gate)
{
- task = _queue.ScheduleTask(() =>
- {
- action();
- }, cancellationToken).CompletesAsyncOperation(asyncToken);
+ task = _queue.ScheduleTaskInProgress(action, cancellationToken).CompletesAsyncOperation(asyncToken);
_tasks.Add(task);
}
task.Wait(cancellationToken);
}
+#pragma warning restore
}
}
diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs
index 38320788d4e6a8d8e79fb0cbcbc84f383cf55591..f5ab4a1107627e920b6cbfc788ee2f74fd3f749f 100644
--- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs
+++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs
@@ -10,8 +10,10 @@
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Diagnostics.EngineV2;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
@@ -24,6 +26,14 @@ namespace Microsoft.CodeAnalysis.Diagnostics
[Shared]
internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
{
+ private const string DiagnosticsUpdatedEventName = "DiagnosticsUpdated";
+
+ private static readonly DiagnosticEventTaskScheduler s_eventScheduler = new DiagnosticEventTaskScheduler(blockingUpperBound: 100);
+
+ // use eventMap and taskQueue to serialize events
+ private readonly EventMap _eventMap;
+ private readonly TaskQueue _eventQueue;
+
public DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; private set; }
public HostDiagnosticAnalyzers HostAnalyzers { get; private set; }
@@ -31,6 +41,9 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
public IAsynchronousOperationListener Listener { get; }
+ private readonly ConditionalWeakTable _map;
+ private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer;
+
[ImportingConstructor]
public DiagnosticAnalyzerService(
IDiagnosticUpdateSourceRegistrationService registrationService,
@@ -72,12 +85,22 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
AbstractHostDiagnosticUpdateSource? hostDiagnosticUpdateSource,
IDiagnosticUpdateSourceRegistrationService registrationService,
IAsynchronousOperationListener? listener = null)
- : this(registrationService)
{
AnalyzerInfoCache = analyzerInfoCache;
HostAnalyzers = hostAnalyzers;
_hostDiagnosticUpdateSource = hostDiagnosticUpdateSource;
+
+ _map = new ConditionalWeakTable();
+ _createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback;
+
Listener = listener ?? AsynchronousOperationListenerProvider.NullListener;
+ _eventMap = new EventMap();
+
+ // use diagnostic event task scheduler so that we never flood async events queue with million of events.
+ // queue itself can handle huge number of events but we are seeing OOM due to captured data in pending events.
+ _eventQueue = new TaskQueue(Listener, s_eventScheduler);
+
+ registrationService.Register(this);
}
private static ImmutableArray GetHostDiagnosticAnalyzerPackage(IHostDiagnosticAnalyzerPackageProvider? diagnosticAnalyzerProviderService)
diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs
index 6c2efc4534882b124aec28cbee93622d4ce5eaf8..f319e35183aee4ae8b3a7615ea7621e317106b54 100644
--- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs
+++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs
@@ -17,16 +17,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics
workspaceKinds: new string[] { WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.AnyCodeRoslynWorkspace })]
internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider
{
- private readonly ConditionalWeakTable _map;
- private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer;
-
- [SuppressMessage("RoslyDiagnosticsReliability", "RS0034:Exported parts should have [ImportingConstructor]", Justification = "Private constructor used for deterministic field initialization")]
- private DiagnosticAnalyzerService()
- {
- _map = new ConditionalWeakTable();
- _createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback;
- }
-
public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
{
if (!workspace.Options.GetOption(ServiceComponentOnOffOptions.DiagnosticProvider))
diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs
index 210d88dc7af54cb997de55769f8d0b960c7069f1..443086062a4233c181747f8e6e8c8e9a89fb3c5c 100644
--- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs
+++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs
@@ -14,26 +14,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics
{
internal partial class DiagnosticAnalyzerService : IDiagnosticUpdateSource
{
- private const string DiagnosticsUpdatedEventName = "DiagnosticsUpdated";
-
- private static readonly DiagnosticEventTaskScheduler s_eventScheduler = new DiagnosticEventTaskScheduler(blockingUpperBound: 100);
-
- // use eventMap and taskQueue to serialize events
- private readonly EventMap _eventMap;
- private readonly SimpleTaskQueue _eventQueue;
-
- [SuppressMessage("RoslyDiagnosticsReliability", "RS0034:Exported parts should have [ImportingConstructor]", Justification = "Private constructor used for deterministic field initialization")]
- private DiagnosticAnalyzerService(IDiagnosticUpdateSourceRegistrationService registrationService) : this()
- {
- _eventMap = new EventMap();
-
- // use diagnostic event task scheduler so that we never flood async events queue with million of events.
- // queue itself can handle huge number of events but we are seeing OOM due to captured data in pending events.
- _eventQueue = new SimpleTaskQueue(s_eventScheduler);
-
- registrationService.Register(this);
- }
-
public event EventHandler DiagnosticsUpdated
{
add
@@ -66,8 +46,7 @@ internal void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
var ev = _eventMap.GetEventHandlers>(DiagnosticsUpdatedEventName);
if (ev.HasHandlers)
{
- var asyncToken = Listener.BeginAsyncOperation(nameof(RaiseDiagnosticsUpdated));
- _eventQueue.ScheduleTask(() => ev.RaiseEvent(handler => handler(this, args))).CompletesAsyncOperation(asyncToken);
+ _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => ev.RaiseEvent(handler => handler(this, args)), CancellationToken.None);
}
}
@@ -82,8 +61,7 @@ internal void RaiseBulkDiagnosticsUpdated(Action>
// this is to reduce for such case to happen.
void raiseEvents(DiagnosticsUpdatedArgs args) => ev.RaiseEvent(handler => handler(this, args));
- var asyncToken = Listener.BeginAsyncOperation(nameof(RaiseDiagnosticsUpdated));
- _eventQueue.ScheduleTask(() => eventAction(raiseEvents)).CompletesAsyncOperation(asyncToken);
+ _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => eventAction(raiseEvents), CancellationToken.None);
}
}
@@ -98,8 +76,7 @@ internal void RaiseBulkDiagnosticsUpdated(Func, T
// this is to reduce for such case to happen.
void raiseEvents(DiagnosticsUpdatedArgs args) => ev.RaiseEvent(handler => handler(this, args));
- var asyncToken = Listener.BeginAsyncOperation(nameof(RaiseDiagnosticsUpdated));
- _eventQueue.ScheduleTask(() => eventActionAsync(raiseEvents)).CompletesAsyncOperation(asyncToken);
+ _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => eventActionAsync(raiseEvents), CancellationToken.None);
}
}
diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs
index 45a5b9a6a4fcab08b9e5a3974370655b73410107..0bc72291d186b884dfc485e5b5bbc75aab2d4fc5 100644
--- a/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs
+++ b/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs
@@ -23,9 +23,8 @@ internal partial class DiagnosticService : IDiagnosticService
private static readonly DiagnosticEventTaskScheduler s_eventScheduler = new DiagnosticEventTaskScheduler(blockingUpperBound: 100);
- private readonly IAsynchronousOperationListener _listener;
private readonly EventMap _eventMap;
- private readonly SimpleTaskQueue _eventQueue;
+ private readonly TaskQueue _eventQueue;
private readonly object _gate;
private readonly Dictionary>> _map;
@@ -42,9 +41,7 @@ internal partial class DiagnosticService : IDiagnosticService
// use diagnostic event task scheduler so that we never flood async events queue with million of events.
// queue itself can handle huge number of events but we are seeing OOM due to captured data in pending events.
- _eventQueue = new SimpleTaskQueue(s_eventScheduler);
-
- _listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService);
+ _eventQueue = new TaskQueue(listenerProvider.GetListener(FeatureAttribute.DiagnosticService), s_eventScheduler);
_gate = new object();
_map = new Dictionary>>();
@@ -71,8 +68,7 @@ private void RaiseDiagnosticsUpdated(IDiagnosticUpdateSource source, Diagnostics
var ev = _eventMap.GetEventHandlers>(DiagnosticsUpdatedEventName);
- var eventToken = _listener.BeginAsyncOperation(DiagnosticsUpdatedEventName);
- _eventQueue.ScheduleTask(() =>
+ _eventQueue.ScheduleTask(DiagnosticsUpdatedEventName, () =>
{
if (!UpdateDataMap(source, args))
{
@@ -81,15 +77,14 @@ private void RaiseDiagnosticsUpdated(IDiagnosticUpdateSource source, Diagnostics
}
ev.RaiseEvent(handler => handler(source, args));
- }).CompletesAsyncOperation(eventToken);
+ }, CancellationToken.None);
}
private void RaiseDiagnosticsCleared(IDiagnosticUpdateSource source)
{
var ev = _eventMap.GetEventHandlers>(DiagnosticsUpdatedEventName);
- var eventToken = _listener.BeginAsyncOperation(DiagnosticsUpdatedEventName);
- _eventQueue.ScheduleTask(() =>
+ _eventQueue.ScheduleTask(DiagnosticsUpdatedEventName, () =>
{
using var pooledObject = SharedPools.Default>().GetPooledObject();
@@ -106,7 +101,7 @@ private void RaiseDiagnosticsCleared(IDiagnosticUpdateSource source)
{
ev.RaiseEvent(handler => handler(source, args));
}
- }).CompletesAsyncOperation(eventToken);
+ }, CancellationToken.None);
}
private bool UpdateDataMap(IDiagnosticUpdateSource source, DiagnosticsUpdatedArgs args)
diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs
index 2fa3065125d73d74ba5a2d2860a683a9ab485c4d..5cde1ebbe7d26e5f84656f231dfcbe25f528287c 100644
--- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs
+++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs
@@ -32,7 +32,7 @@ private partial class WorkCoordinator
private readonly CancellationTokenSource _shutdownNotificationSource;
private readonly CancellationToken _shutdownToken;
- private readonly SimpleTaskQueue _eventProcessingQueue;
+ private readonly TaskQueue _eventProcessingQueue;
// points to processor task
private readonly IncrementalAnalyzerProcessor _documentAndProjectWorkerProcessor;
@@ -59,7 +59,7 @@ private partial class WorkCoordinator
_shutdownNotificationSource = new CancellationTokenSource();
_shutdownToken = _shutdownNotificationSource.Token;
- _eventProcessingQueue = new SimpleTaskQueue(TaskScheduler.Default);
+ _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default);
var activeFileBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS);
var allFilesWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS);
@@ -179,13 +179,11 @@ private void ReanalyzeOnOptionChange(object sender, OptionChangedEventArgs e)
{
// get off from option changed event handler since it runs on UI thread
// getting analyzer can be slow for the very first time since it is lazily initialized
- var asyncToken = _listener.BeginAsyncOperation("ReanalyzeOnOptionChange");
-
- // Force analyze all analyzers if background analysis scope has changed.
- var forceAnalyze = e.Option == SolutionCrawlerOptions.BackgroundAnalysisScopeOption;
-
- _eventProcessingQueue.ScheduleTask(() =>
+ _eventProcessingQueue.ScheduleTask(nameof(ReanalyzeOnOptionChange), () =>
{
+ // Force analyze all analyzers if background analysis scope has changed.
+ var forceAnalyze = e.Option == SolutionCrawlerOptions.BackgroundAnalysisScopeOption;
+
// let each analyzer decide what they want on option change
foreach (var analyzer in _documentAndProjectWorkerProcessor.Analyzers)
{
@@ -195,14 +193,13 @@ private void ReanalyzeOnOptionChange(object sender, OptionChangedEventArgs e)
Reanalyze(analyzer, scope);
}
}
- }, _shutdownToken).CompletesAsyncOperation(asyncToken);
+ }, _shutdownToken);
}
public void Reanalyze(IIncrementalAnalyzer analyzer, ReanalyzeScope scope, bool highPriority = false)
{
- var asyncToken = _listener.BeginAsyncOperation("Reanalyze");
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAsync(analyzer, scope, highPriority), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask("Reanalyze",
+ () => EnqueueWorkItemAsync(analyzer, scope, highPriority), _shutdownToken);
if (scope.HasMultipleDocuments)
{
@@ -216,7 +213,6 @@ public void Reanalyze(IIncrementalAnalyzer analyzer, ReanalyzeScope scope, bool
private void OnActiveDocumentChanged(object sender, DocumentId activeDocumentId)
{
- IAsyncToken asyncToken;
var solution = _registration.Workspace.CurrentSolution;
// Check if we are only performing backgroung analysis for active file.
@@ -237,15 +233,13 @@ private void OnActiveDocumentChanged(object sender, DocumentId activeDocumentId)
{
if (_lastActiveDocument != null)
{
- asyncToken = _listener.BeginAsyncOperation("OnDocumentClosed");
- EnqueueEvent(_lastActiveDocument.Project.Solution, _lastActiveDocument.Id, InvocationReasons.DocumentClosed, asyncToken);
+ EnqueueEvent(_lastActiveDocument.Project.Solution, _lastActiveDocument.Id, InvocationReasons.DocumentClosed, "OnDocumentClosed");
}
_lastActiveDocument = activeDocument;
}
- asyncToken = _listener.BeginAsyncOperation("OnDocumentOpened");
- EnqueueEvent(activeDocument.Project.Solution, activeDocument.Id, InvocationReasons.DocumentOpened, asyncToken);
+ EnqueueEvent(activeDocument.Project.Solution, activeDocument.Id, InvocationReasons.DocumentOpened, "OnDocumentOpened");
}
}
}
@@ -255,7 +249,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
// guard us from cancellation
try
{
- ProcessEvents(args, _listener.BeginAsyncOperation("OnWorkspaceChanged"));
+ ProcessEvent(args, "OnWorkspaceChanged");
}
catch (OperationCanceledException oce)
{
@@ -287,7 +281,7 @@ private bool NotOurShutdownToken(OperationCanceledException oce)
return oce.CancellationToken == _shutdownToken;
}
- private void ProcessEvents(WorkspaceChangeEventArgs args, IAsyncToken asyncToken)
+ private void ProcessEvent(WorkspaceChangeEventArgs args, string eventName)
{
SolutionCrawlerLogger.LogWorkspaceEvent(_logAggregator, (int)args.Kind);
@@ -299,13 +293,13 @@ private void ProcessEvents(WorkspaceChangeEventArgs args, IAsyncToken asyncToken
case WorkspaceChangeKind.SolutionReloaded:
case WorkspaceChangeKind.SolutionRemoved:
case WorkspaceChangeKind.SolutionCleared:
- ProcessSolutionEvent(args, asyncToken);
+ ProcessSolutionEvent(args, eventName);
break;
case WorkspaceChangeKind.ProjectAdded:
case WorkspaceChangeKind.ProjectChanged:
case WorkspaceChangeKind.ProjectReloaded:
case WorkspaceChangeKind.ProjectRemoved:
- ProcessProjectEvent(args, asyncToken);
+ ProcessProjectEvent(args, eventName);
break;
case WorkspaceChangeKind.DocumentAdded:
case WorkspaceChangeKind.DocumentReloaded:
@@ -319,7 +313,7 @@ private void ProcessEvents(WorkspaceChangeEventArgs args, IAsyncToken asyncToken
case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved:
case WorkspaceChangeKind.AnalyzerConfigDocumentChanged:
case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded:
- ProcessDocumentEvent(args, asyncToken);
+ ProcessDocumentEvent(args, eventName);
break;
default:
throw ExceptionUtilities.UnexpectedValue(args.Kind);
@@ -328,31 +322,29 @@ private void ProcessEvents(WorkspaceChangeEventArgs args, IAsyncToken asyncToken
private void OnDocumentOpened(object sender, DocumentEventArgs e)
{
- var asyncToken = _listener.BeginAsyncOperation("OnDocumentOpened");
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAsync(e.Document, InvocationReasons.DocumentOpened), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask("OnDocumentOpened",
+ () => EnqueueWorkItemAsync(e.Document, InvocationReasons.DocumentOpened), _shutdownToken);
}
private void OnDocumentClosed(object sender, DocumentEventArgs e)
{
- var asyncToken = _listener.BeginAsyncOperation("OnDocumentClosed");
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAsync(e.Document, InvocationReasons.DocumentClosed), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask("OnDocumentClosed",
+ () => EnqueueWorkItemAsync(e.Document, InvocationReasons.DocumentClosed), _shutdownToken);
}
- private void ProcessDocumentEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncToken)
+ private void ProcessDocumentEvent(WorkspaceChangeEventArgs e, string eventName)
{
switch (e.Kind)
{
case WorkspaceChangeKind.DocumentAdded:
- EnqueueEvent(e.NewSolution, e.DocumentId, InvocationReasons.DocumentAdded, asyncToken);
+ EnqueueEvent(e.NewSolution, e.DocumentId, InvocationReasons.DocumentAdded, eventName);
break;
case WorkspaceChangeKind.DocumentRemoved:
- EnqueueEvent(e.OldSolution, e.DocumentId, InvocationReasons.DocumentRemoved, asyncToken);
+ EnqueueEvent(e.OldSolution, e.DocumentId, InvocationReasons.DocumentRemoved, eventName);
break;
case WorkspaceChangeKind.DocumentReloaded:
case WorkspaceChangeKind.DocumentChanged:
- EnqueueEvent(e.OldSolution, e.NewSolution, e.DocumentId, asyncToken);
+ EnqueueEvent(e.OldSolution, e.NewSolution, e.DocumentId, eventName);
break;
case WorkspaceChangeKind.AdditionalDocumentAdded:
@@ -364,7 +356,7 @@ private void ProcessDocumentEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncT
case WorkspaceChangeKind.AnalyzerConfigDocumentChanged:
case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded:
// If an additional file or .editorconfig has changed we need to reanalyze the entire project.
- EnqueueEvent(e.NewSolution, e.ProjectId, InvocationReasons.AdditionalDocumentChanged, asyncToken);
+ EnqueueEvent(e.NewSolution, e.ProjectId, InvocationReasons.AdditionalDocumentChanged, eventName);
break;
default:
@@ -372,82 +364,82 @@ private void ProcessDocumentEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncT
}
}
- private void ProcessProjectEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncToken)
+ private void ProcessProjectEvent(WorkspaceChangeEventArgs e, string eventName)
{
switch (e.Kind)
{
case WorkspaceChangeKind.ProjectAdded:
- EnqueueEvent(e.NewSolution, e.ProjectId, InvocationReasons.DocumentAdded, asyncToken);
+ EnqueueEvent(e.NewSolution, e.ProjectId, InvocationReasons.DocumentAdded, eventName);
break;
case WorkspaceChangeKind.ProjectRemoved:
- EnqueueEvent(e.OldSolution, e.ProjectId, InvocationReasons.DocumentRemoved, asyncToken);
+ EnqueueEvent(e.OldSolution, e.ProjectId, InvocationReasons.DocumentRemoved, eventName);
break;
case WorkspaceChangeKind.ProjectChanged:
case WorkspaceChangeKind.ProjectReloaded:
- EnqueueEvent(e.OldSolution, e.NewSolution, e.ProjectId, asyncToken);
+ EnqueueEvent(e.OldSolution, e.NewSolution, e.ProjectId, eventName);
break;
default:
throw ExceptionUtilities.UnexpectedValue(e.Kind);
}
}
- private void ProcessSolutionEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncToken)
+ private void ProcessSolutionEvent(WorkspaceChangeEventArgs e, string eventName)
{
switch (e.Kind)
{
case WorkspaceChangeKind.SolutionAdded:
- EnqueueEvent(e.NewSolution, InvocationReasons.DocumentAdded, asyncToken);
+ EnqueueEvent(e.NewSolution, InvocationReasons.DocumentAdded, eventName);
break;
case WorkspaceChangeKind.SolutionRemoved:
- EnqueueEvent(e.OldSolution, InvocationReasons.SolutionRemoved, asyncToken);
+ EnqueueEvent(e.OldSolution, InvocationReasons.SolutionRemoved, eventName);
break;
case WorkspaceChangeKind.SolutionCleared:
- EnqueueEvent(e.OldSolution, InvocationReasons.DocumentRemoved, asyncToken);
+ EnqueueEvent(e.OldSolution, InvocationReasons.DocumentRemoved, eventName);
break;
case WorkspaceChangeKind.SolutionChanged:
case WorkspaceChangeKind.SolutionReloaded:
- EnqueueEvent(e.OldSolution, e.NewSolution, asyncToken);
+ EnqueueEvent(e.OldSolution, e.NewSolution, eventName);
break;
default:
throw ExceptionUtilities.UnexpectedValue(e.Kind);
}
}
- private void EnqueueEvent(Solution oldSolution, Solution newSolution, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution oldSolution, Solution newSolution, string eventName)
{
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAsync(oldSolution, newSolution), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemAsync(oldSolution, newSolution), _shutdownToken);
}
- private void EnqueueEvent(Solution solution, InvocationReasons invocationReasons, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution solution, InvocationReasons invocationReasons, string eventName)
{
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemForSolutionAsync(solution, invocationReasons), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemForSolutionAsync(solution, invocationReasons), _shutdownToken);
}
- private void EnqueueEvent(Solution oldSolution, Solution newSolution, ProjectId projectId, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution oldSolution, Solution newSolution, ProjectId projectId, string eventName)
{
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAfterDiffAsync(oldSolution, newSolution, projectId), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemAfterDiffAsync(oldSolution, newSolution, projectId), _shutdownToken);
}
- private void EnqueueEvent(Solution solution, ProjectId projectId, InvocationReasons invocationReasons, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution solution, ProjectId projectId, InvocationReasons invocationReasons, string eventName)
{
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemForProjectAsync(solution, projectId, invocationReasons), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemForProjectAsync(solution, projectId, invocationReasons), _shutdownToken);
}
- private void EnqueueEvent(Solution solution, DocumentId documentId, InvocationReasons invocationReasons, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution solution, DocumentId documentId, InvocationReasons invocationReasons, string eventName)
{
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemForDocumentAsync(solution, documentId, invocationReasons), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemForDocumentAsync(solution, documentId, invocationReasons), _shutdownToken);
}
- private void EnqueueEvent(Solution oldSolution, Solution newSolution, DocumentId documentId, IAsyncToken asyncToken)
+ private void EnqueueEvent(Solution oldSolution, Solution newSolution, DocumentId documentId, string eventName)
{
// document changed event is the special one.
- _eventProcessingQueue.ScheduleTask(
- () => EnqueueWorkItemAfterDiffAsync(oldSolution, newSolution, documentId), _shutdownToken).CompletesAsyncOperation(asyncToken);
+ _eventProcessingQueue.ScheduleTask(eventName,
+ () => EnqueueWorkItemAfterDiffAsync(oldSolution, newSolution, documentId), _shutdownToken);
}
private async Task EnqueueWorkItemAsync(Document document, InvocationReasons invocationReasons, SyntaxNode changedMember = null)
diff --git a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs
index bc1cf74982e2051deb14e295ad0051d6fb70e892..c35617a8434985c8dff5c42685f6e7562ead61ca 100644
--- a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs
+++ b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs
@@ -14,10 +14,10 @@
namespace Microsoft.CodeAnalysis.Host
{
- internal class BackgroundCompiler : IDisposable
+ internal sealed class BackgroundCompiler : IDisposable
{
private Workspace _workspace;
- private readonly IWorkspaceTaskScheduler _compilationScheduler;
+ private readonly TaskQueue _taskQueue;
// Used to keep a strong reference to the built compilations so they are not GC'd
private Compilation[] _mostRecentCompilations;
@@ -30,8 +30,8 @@ public BackgroundCompiler(Workspace workspace)
_workspace = workspace;
// make a scheduler that runs on the thread pool
- var taskSchedulerFactory = workspace.Services.GetService();
- _compilationScheduler = taskSchedulerFactory.CreateBackgroundTaskScheduler();
+ var listenerProvider = workspace.Services.GetRequiredService();
+ _taskQueue = new TaskQueue(listenerProvider.GetListener(), TaskScheduler.Default);
_cancellationSource = new CancellationTokenSource();
_workspace.WorkspaceChanged += OnWorkspaceChanged;
@@ -131,9 +131,9 @@ private void CancelBuild(bool releasePreviousCompilations)
ISet allProjects)
{
var cancellationToken = _cancellationSource.Token;
- return _compilationScheduler.ScheduleTask(
- () => BuildCompilationsAsync(solution, initialProject, allProjects, cancellationToken),
+ return _taskQueue.ScheduleTask(
"BackgroundCompiler.BuildCompilationsAsync",
+ () => BuildCompilationsAsync(solution, initialProject, allProjects, cancellationToken),
cancellationToken);
}
diff --git a/src/Features/Core/Portable/Workspace/BackgroundParser.cs b/src/Features/Core/Portable/Workspace/BackgroundParser.cs
index 81c039a2f3da1400f1fc98a414737b38e5e9e034..90cd9570356ca44a977e6576d914158dfcf62861 100644
--- a/src/Features/Core/Portable/Workspace/BackgroundParser.cs
+++ b/src/Features/Core/Portable/Workspace/BackgroundParser.cs
@@ -21,10 +21,10 @@ namespace Microsoft.CodeAnalysis.Host
/// but certain host such as VS, we have this (BackgroundParser) which preemptively
/// trying to realize such trees for open/active files expecting users will use them soonish.
///
- internal class BackgroundParser
+ internal sealed class BackgroundParser
{
private readonly Workspace _workspace;
- private readonly IWorkspaceTaskScheduler _taskScheduler;
+ private readonly TaskQueue _taskQueue;
private readonly IDocumentTrackingService _documentTrackingService;
private readonly ReaderWriterLockSlim _stateLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
@@ -38,8 +38,8 @@ public BackgroundParser(Workspace workspace)
{
_workspace = workspace;
- var taskSchedulerFactory = workspace.Services.GetService();
- _taskScheduler = taskSchedulerFactory.CreateBackgroundTaskScheduler();
+ var listenerProvider = workspace.Services.GetRequiredService();
+ _taskQueue = new TaskQueue(listenerProvider.GetListener(), TaskScheduler.Default);
_documentTrackingService = workspace.Services.GetService();
@@ -212,9 +212,9 @@ private Task ParseDocumentAsync(Document document)
// By not cancelling, we can reuse the useful results of previous tasks when performing later steps in the chain.
//
// we still cancel whole task if the task didn't start yet. we just don't cancel if task is started but not finished yet.
- var task = _taskScheduler.ScheduleTask(
- () => document.GetSyntaxTreeAsync(CancellationToken.None),
+ var task = _taskQueue.ScheduleTask(
"BackgroundParser.ParseDocumentAsync",
+ () => document.GetSyntaxTreeAsync(CancellationToken.None),
cancellationToken);
// Always ensure that we mark this work as done from the workmap.
diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs
index 6e9b08cf7ee050f49232051772eecdcf721de309..7090fc6c6c65e7d16f324e46a9466935c2cbbe11 100644
--- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs
+++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.SolutionChecksumUpdater.cs
@@ -19,7 +19,7 @@ internal partial class RemoteHostClientServiceFactory
private class SolutionChecksumUpdater : GlobalOperationAwareIdleProcessor
{
private readonly RemoteHostClientService _service;
- private readonly SimpleTaskQueue _textChangeQueue;
+ private readonly TaskQueue _textChangeQueue;
private readonly SemaphoreSlim _event;
private readonly object _gate;
@@ -34,7 +34,7 @@ public SolutionChecksumUpdater(RemoteHostClientService service, CancellationToke
service.Workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS), shutdownToken)
{
_service = service;
- _textChangeQueue = new SimpleTaskQueue(TaskScheduler.Default);
+ _textChangeQueue = new TaskQueue(service.Listener, TaskScheduler.Default);
_event = new SemaphoreSlim(initialCount: 0);
_gate = new object();
@@ -199,8 +199,7 @@ private void PushTextChanges(Document oldDocument, Document newDocument)
}
// only cancelled when remote host gets shutdown
- var token = Listener.BeginAsyncOperation(nameof(PushTextChanges));
- _textChangeQueue.ScheduleTask(async () =>
+ _textChangeQueue.ScheduleTask(nameof(PushTextChanges), async () =>
{
var client = await RemoteHostClient.TryGetClientAsync(_service.Workspace, CancellationToken).ConfigureAwait(false);
if (client == null)
@@ -218,7 +217,7 @@ private void PushTextChanges(Document oldDocument, Document newDocument)
callbackTarget: null,
CancellationToken).ConfigureAwait(false);
- }, CancellationToken).CompletesAsyncOperation(token);
+ }, CancellationToken);
}
}
}
diff --git a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs
index 8f094332339fd2978b5b3c00787d13759a8a3b45..90df4d4276e12d3e19090cc7709d1b807ffc88b9 100644
--- a/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs
+++ b/src/VisualStudio/Core/Def/Implementation/TaskList/ExternalErrorDiagnosticUpdateSource.cs
@@ -26,8 +26,7 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou
private readonly IDiagnosticAnalyzerService _diagnosticService;
private readonly IGlobalOperationNotificationService _notificationService;
- private readonly SimpleTaskQueue _taskQueue;
- private readonly IAsynchronousOperationListener _listener;
+ private readonly TaskQueue _taskQueue;
private readonly object _gate = new object();
private InProgressState _stateDoNotAccessDirectly = null;
@@ -52,8 +51,7 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou
IAsynchronousOperationListener listener)
{
// use queue to serialize work. no lock needed
- _taskQueue = new SimpleTaskQueue(TaskScheduler.Default);
- _listener = listener;
+ _taskQueue = new TaskQueue(listener, TaskScheduler.Default);
_workspace = workspace;
_workspace.WorkspaceChanged += OnWorkspaceChanged;
@@ -84,8 +82,7 @@ public void ClearErrors(ProjectId projectId)
// capture state if it exists
var state = BuildInprogressState;
- var asyncToken = _listener.BeginAsyncOperation("ClearErrors");
- _taskQueue.ScheduleTask(() =>
+ _taskQueue.ScheduleTask(nameof(ClearErrors), () =>
{
// this will get called if the project is actually built by "build" command.
// we track what project has been built, so that later we can clear any stale live errors
@@ -93,7 +90,7 @@ public void ClearErrors(ProjectId projectId)
state?.Built(projectId);
ClearProjectErrors(state?.Solution ?? _workspace.CurrentSolution, projectId);
- }).CompletesAsyncOperation(asyncToken);
+ }, CancellationToken.None);
}
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
@@ -104,27 +101,18 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.SolutionRemoved:
case WorkspaceChangeKind.SolutionCleared:
case WorkspaceChangeKind.SolutionReloaded:
- {
- var asyncToken = _listener.BeginAsyncOperation("OnSolutionChanged");
- _taskQueue.ScheduleTask(() => e.OldSolution.ProjectIds.Do(p => ClearProjectErrors(e.OldSolution, p))).CompletesAsyncOperation(asyncToken);
- break;
- }
+ _taskQueue.ScheduleTask("OnSolutionChanged", () => e.OldSolution.ProjectIds.Do(p => ClearProjectErrors(e.OldSolution, p)), CancellationToken.None);
+ break;
case WorkspaceChangeKind.ProjectRemoved:
case WorkspaceChangeKind.ProjectReloaded:
- {
- var asyncToken = _listener.BeginAsyncOperation("OnProjectChanged");
- _taskQueue.ScheduleTask(() => ClearProjectErrors(e.OldSolution, e.ProjectId)).CompletesAsyncOperation(asyncToken);
- break;
- }
+ _taskQueue.ScheduleTask("OnProjectChanged", () => ClearProjectErrors(e.OldSolution, e.ProjectId), CancellationToken.None);
+ break;
case WorkspaceChangeKind.DocumentRemoved:
case WorkspaceChangeKind.DocumentReloaded:
- {
- var asyncToken = _listener.BeginAsyncOperation("OnDocumentRemoved");
- _taskQueue.ScheduleTask(() => ClearDocumentErrors(e.OldSolution, e.ProjectId, e.DocumentId)).CompletesAsyncOperation(asyncToken);
- break;
- }
+ _taskQueue.ScheduleTask("OnDocumentRemoved", () => ClearDocumentErrors(e.OldSolution, e.ProjectId, e.DocumentId), CancellationToken.None);
+ break;
case WorkspaceChangeKind.ProjectAdded:
case WorkspaceChangeKind.DocumentAdded:
@@ -140,6 +128,7 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.AnalyzerConfigDocumentChanged:
case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded:
break;
+
default:
throw ExceptionUtilities.UnexpectedValue(e.Kind);
}
@@ -159,8 +148,7 @@ internal void OnSolutionBuildCompleted()
var inProgressState = ClearInProgressState();
// enqueue build/live sync in the queue.
- var asyncToken = _listener.BeginAsyncOperation("OnSolutionBuild");
- _taskQueue.ScheduleTask(async () =>
+ _taskQueue.ScheduleTask("OnSolutionBuild", async () =>
{
// nothing to do
if (inProgressState == null)
@@ -187,7 +175,7 @@ internal void OnSolutionBuildCompleted()
}
inProgressState.Done();
- }).CompletesAsyncOperation(asyncToken);
+ }, CancellationToken.None);
}
private Task CleanupAllLiveErrorsAsync(DiagnosticAnalyzerService diagnosticService, IEnumerable projects)
@@ -264,11 +252,7 @@ public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic)
// capture state that will be processed in background thread.
var state = GetOrCreateInProgressState();
- var asyncToken = _listener.BeginAsyncOperation("Project New Errors");
- _taskQueue.ScheduleTask(() =>
- {
- state.AddError(projectId, diagnostic);
- }).CompletesAsyncOperation(asyncToken);
+ _taskQueue.ScheduleTask("Project New Errors", () => state.AddError(projectId, diagnostic), CancellationToken.None);
}
public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
@@ -276,11 +260,7 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
// capture state that will be processed in background thread.
var state = GetOrCreateInProgressState();
- var asyncToken = _listener.BeginAsyncOperation("Document New Errors");
- _taskQueue.ScheduleTask(() =>
- {
- state.AddError(documentId, diagnostic);
- }).CompletesAsyncOperation(asyncToken);
+ _taskQueue.ScheduleTask("Document New Errors", () => state.AddError(documentId, diagnostic), CancellationToken.None);
}
public void AddNewErrors(
@@ -289,8 +269,7 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
// capture state that will be processed in background thread
var state = GetOrCreateInProgressState();
- var asyncToken = _listener.BeginAsyncOperation("Project New Errors");
- _taskQueue.ScheduleTask(() =>
+ _taskQueue.ScheduleTask("Project New Errors", () =>
{
foreach (var kv in documentErrorMap)
{
@@ -298,7 +277,7 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic)
}
state.AddErrors(projectId, projectErrors);
- }).CompletesAsyncOperation(asyncToken);
+ }, CancellationToken.None);
}
private InProgressState BuildInprogressState
diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerProvider.cs
similarity index 65%
rename from src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerFactory.cs
rename to src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerProvider.cs
index a2a659f501d44fd62d976433a6f9f2a996fa0916..ae1013c37b732f0c3e92ec7bb7cbf193a4f5286d 100644
--- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerFactory.cs
+++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioTaskSchedulerProvider.cs
@@ -5,37 +5,27 @@
using System;
using System.Collections.Generic;
using System.Composition;
-using System.Threading;
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
-using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Threading;
-using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation
{
- [ExportWorkspaceService(typeof(IWorkspaceTaskSchedulerFactory), ServiceLayer.Host), Shared]
- internal class VisualStudioTaskSchedulerFactory : EditorTaskSchedulerFactory
+ [ExportWorkspaceService(typeof(ITaskSchedulerProvider), ServiceLayer.Host), Shared]
+ internal sealed class VisualStudioTaskSchedulerProvider : ITaskSchedulerProvider
{
- private readonly IThreadingContext _threadingContext;
+ public TaskScheduler CurrentContextScheduler { get; }
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public VisualStudioTaskSchedulerFactory(IThreadingContext threadingContext, IAsynchronousOperationListenerProvider listenerProvider)
- : base(listenerProvider)
+ public VisualStudioTaskSchedulerProvider(IThreadingContext threadingContext)
{
- _threadingContext = threadingContext;
+ CurrentContextScheduler = new JoinableTaskFactoryTaskScheduler(threadingContext.JoinableTaskFactory);
}
- public override IWorkspaceTaskScheduler CreateEventingTaskQueue()
- {
- return new WorkspaceTaskQueue(this, new JoinableTaskFactoryTaskScheduler(_threadingContext.JoinableTaskFactory));
- }
-
- private class JoinableTaskFactoryTaskScheduler : TaskScheduler
+ private sealed class JoinableTaskFactoryTaskScheduler : TaskScheduler
{
private readonly JoinableTaskFactory _joinableTaskFactory;
diff --git a/src/Workspaces/Core/Portable/Notification/GlobalOperationNotificationService.cs b/src/Workspaces/Core/Portable/Notification/GlobalOperationNotificationService.cs
index 940d01efe6080417e0bfe83f33b4292087fa7c54..cf23397ea5c881b72e0be836f5b73aba56bd9fdb 100644
--- a/src/Workspaces/Core/Portable/Notification/GlobalOperationNotificationService.cs
+++ b/src/Workspaces/Core/Portable/Notification/GlobalOperationNotificationService.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
@@ -20,14 +21,12 @@ internal class GlobalOperationNotificationService : AbstractGlobalOperationNotif
private readonly HashSet _registrations = new HashSet();
private readonly HashSet _operations = new HashSet();
- private readonly SimpleTaskQueue _eventQueue = new SimpleTaskQueue(TaskScheduler.Default);
+ private readonly TaskQueue _eventQueue;
private readonly EventMap _eventMap = new EventMap();
- private readonly IAsynchronousOperationListener _listener;
-
public GlobalOperationNotificationService(IAsynchronousOperationListener listener)
{
- _listener = listener;
+ _eventQueue = new TaskQueue(listener, TaskScheduler.Default);
}
public override GlobalOperationRegistration Start(string operation)
@@ -52,33 +51,24 @@ public override GlobalOperationRegistration Start(string operation)
}
}
- protected virtual Task RaiseGlobalOperationStartedAsync()
+ private Task RaiseGlobalOperationStartedAsync()
{
var ev = _eventMap.GetEventHandlers(GlobalOperationStartedEventName);
if (ev.HasHandlers)
{
- var asyncToken = _listener.BeginAsyncOperation("GlobalOperationStarted");
- return _eventQueue.ScheduleTask(() =>
- {
- ev.RaiseEvent(handler => handler(this, EventArgs.Empty));
- }).CompletesAsyncOperation(asyncToken);
+ return _eventQueue.ScheduleTask(GlobalOperationStartedEventName, () => ev.RaiseEvent(handler => handler(this, EventArgs.Empty)), CancellationToken.None);
}
return Task.CompletedTask;
}
- protected virtual Task RaiseGlobalOperationStoppedAsync(IReadOnlyList operations, bool cancelled)
+ private Task RaiseGlobalOperationStoppedAsync(IReadOnlyList operations, bool cancelled)
{
var ev = _eventMap.GetEventHandlers>(GlobalOperationStoppedEventName);
if (ev.HasHandlers)
{
- var asyncToken = _listener.BeginAsyncOperation("GlobalOperationStopped");
var args = new GlobalOperationEventArgs(operations, cancelled);
-
- return _eventQueue.ScheduleTask(() =>
- {
- ev.RaiseEvent(handler => handler(this, args));
- }).CompletesAsyncOperation(asyncToken);
+ return _eventQueue.ScheduleTask(GlobalOperationStoppedEventName, () => ev.RaiseEvent(handler => handler(this, args)), CancellationToken.None);
}
return Task.CompletedTask;
diff --git a/src/Workspaces/Core/Portable/Options/OptionServiceFactory.cs b/src/Workspaces/Core/Portable/Options/OptionServiceFactory.cs
index 095ec6eff2842e7b781849920da8128f85aada84..5ea0632e260418fc9f727b088036b8e5fb6996fe 100644
--- a/src/Workspaces/Core/Portable/Options/OptionServiceFactory.cs
+++ b/src/Workspaces/Core/Portable/Options/OptionServiceFactory.cs
@@ -13,6 +13,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.TestHooks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Options
@@ -36,14 +37,14 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
///
/// Wraps an underlying and exposes its data to workspace
/// clients. Also takes the notifications
- /// and forwards them along using the same used by the
+ /// and forwards them along using the same used by the
/// this is connected to. i.e. instead of synchronously just passing
/// along the underlying events, these will be enqueued onto the workspace's eventing queue.
///
- internal class OptionService : IWorkspaceOptionService
+ internal sealed class OptionService : IWorkspaceOptionService
{
private readonly IGlobalOptionService _globalOptionService;
- private readonly IWorkspaceTaskScheduler _taskQueue;
+ private readonly TaskQueue _taskQueue;
///
/// Gate guarding and .
@@ -62,8 +63,9 @@ internal class OptionService : IWorkspaceOptionService
{
_globalOptionService = globalOptionService;
- var workspaceTaskSchedulerFactory = workspaceServices.GetRequiredService();
- _taskQueue = workspaceTaskSchedulerFactory.CreateEventingTaskQueue();
+ var schedulerProvider = workspaceServices.GetRequiredService();
+ var listenerProvider = workspaceServices.GetRequiredService();
+ _taskQueue = new TaskQueue(listenerProvider.GetListener(), schedulerProvider.CurrentContextScheduler);
_globalOptionService.OptionChanged += OnGlobalOptionServiceOptionChanged;
}
@@ -77,7 +79,7 @@ public void OnWorkspaceDisposed(Workspace workspace)
private void OnGlobalOptionServiceOptionChanged(object? sender, OptionChangedEventArgs e)
{
- _taskQueue.ScheduleTask(() =>
+ _taskQueue.ScheduleTask(nameof(OptionService) + "." + nameof(OnGlobalOptionServiceOptionChanged), () =>
{
// Ensure we grab the event handlers inside the scheduled task to prevent a race of people unsubscribing
// but getting the event later on the UI thread
@@ -86,7 +88,7 @@ private void OnGlobalOptionServiceOptionChanged(object? sender, OptionChangedEve
{
handler(this, e);
}
- }, "OptionsService.OnGlobalOptionServiceOptionChanged");
+ }, CancellationToken.None);
}
private ImmutableArray> GetEventHandlers()
diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs
index 24e7c40d7bd91ba89dbc63f6ca0c9a8af3508c1f..1fadc0749dd61ce4a0530ca9e9dea58271a01a2d 100644
--- a/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs
+++ b/src/Workspaces/Core/Portable/Shared/TestHooks/IAsynchronousOperationListenerProvider.cs
@@ -232,7 +232,7 @@ private IEnumerable GetCandidateWaiters(string[] f
return _singletonListeners.Where(kv => featureNames.Contains(kv.Key)).Select(kv => (IAsynchronousOperationWaiter)kv.Value);
}
- private class NullOperationListener : IAsynchronousOperationListener
+ private sealed class NullOperationListener : IAsynchronousOperationListener
{
public IAsyncToken BeginAsyncOperation(
string name,
@@ -247,7 +247,7 @@ public async Task Delay(TimeSpan delay, CancellationToken cancellationToke
}
}
- private class NullListenerProvider : IAsynchronousOperationListenerProvider
+ private sealed class NullListenerProvider : IAsynchronousOperationListenerProvider
{
public IAsynchronousOperationListener GetListener(string featureName) => NullListener;
}
diff --git a/src/Workspaces/Core/Portable/Utilities/TaskQueue.cs b/src/Workspaces/Core/Portable/Utilities/TaskQueue.cs
new file mode 100644
index 0000000000000000000000000000000000000000..965929a6b6c01230218b6f7b8d6925cdf4dc2031
--- /dev/null
+++ b/src/Workspaces/Core/Portable/Utilities/TaskQueue.cs
@@ -0,0 +1,128 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Shared.TestHooks;
+
+namespace Roslyn.Utilities
+{
+ ///
+ /// Implements a queue of asynchronously executed tasks.
+ ///
+ internal sealed class TaskQueue
+ {
+ public IAsynchronousOperationListener Listener { get; }
+ public TaskScheduler Scheduler { get; }
+
+ private readonly object _gate = new object();
+ private Task _latestTask;
+
+ public TaskQueue(IAsynchronousOperationListener operationListener, TaskScheduler taskScheduler)
+ {
+ Contract.ThrowIfNull(operationListener);
+ Contract.ThrowIfNull(taskScheduler);
+
+ Listener = operationListener;
+ Scheduler = taskScheduler;
+ _latestTask = Task.CompletedTask;
+ }
+
+ public Task LastScheduledTask => _latestTask;
+
+ private IAsyncToken BeginOperation(string taskName)
+ => Listener.BeginAsyncOperation(taskName);
+
+ private TTask EndOperation(IAsyncToken token, TTask task) where TTask : Task
+ {
+ // send the notification on operation being complete but do not wait for the notification to be delivered
+ _ = task.CompletesAsyncOperation(token);
+
+ return task;
+ }
+
+#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods (Task wrappers, not asynchronous methods)
+#pragma warning disable CS0618 // Type or member is obsolete (https://github.com/dotnet/roslyn/issues/42742)
+ ///
+ /// Enqueue specified and notify of its start and completion.
+ ///
+ /// The that executes the operation.
+ public Task ScheduleTask(string taskName, Action operation, CancellationToken cancellationToken)
+ => EndOperation(BeginOperation(taskName), ScheduleTaskInProgress(operation, cancellationToken));
+
+ ///
+ public Task ScheduleTask(string taskName, Func operation, CancellationToken cancellationToken)
+ => EndOperation(BeginOperation(taskName), ScheduleTaskInProgress(operation, cancellationToken));
+
+ ///
+ public Task ScheduleTask(string taskName, Func operation, CancellationToken cancellationToken)
+ => EndOperation(BeginOperation(taskName), ScheduleTaskInProgress(operation, cancellationToken));
+
+ ///
+ public Task ScheduleTask(string taskName, Func> operation, CancellationToken cancellationToken)
+ => EndOperation(BeginOperation(taskName), ScheduleTaskInProgress(operation, cancellationToken));
+
+ ///
+ /// Enqueue specified .
+ /// Assumes has already been notified of its start and will be notified when it completes.
+ ///
+ /// The that executes the operation.
+ [PerformanceSensitive("https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html", AllowCaptures = false)]
+ [Obsolete("Should be private: https://github.com/dotnet/roslyn/issues/42742")]
+ public Task ScheduleTaskInProgress(Action operation, CancellationToken cancellationToken)
+ {
+ lock (_gate)
+ {
+ var task = _latestTask.SafeContinueWith(_ => operation(), cancellationToken, TaskContinuationOptions.None, Scheduler);
+ _latestTask = task;
+ return task;
+ }
+ }
+
+ ///
+ [PerformanceSensitive("https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html", AllowCaptures = false)]
+ [Obsolete("Should be private: https://github.com/dotnet/roslyn/issues/42742")]
+ public Task ScheduleTaskInProgress(Func operation, CancellationToken cancellationToken)
+ {
+ lock (_gate)
+ {
+ var task = _latestTask.SafeContinueWith(_ => operation(), cancellationToken, TaskContinuationOptions.None, Scheduler);
+ _latestTask = task;
+ return task;
+ }
+ }
+
+ ///
+ [PerformanceSensitive("https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html", AllowCaptures = false)]
+ [Obsolete("Should be private: https://github.com/dotnet/roslyn/issues/42742")]
+ public Task ScheduleTaskInProgress(Func operation, CancellationToken cancellationToken)
+ {
+ lock (_gate)
+ {
+ var task = _latestTask.SafeContinueWithFromAsync(_ => operation(), cancellationToken, TaskContinuationOptions.None, Scheduler);
+ _latestTask = task;
+ return task;
+ }
+ }
+
+ ///
+ [PerformanceSensitive("https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html", AllowCaptures = false)]
+ [Obsolete("Should be private: https://github.com/dotnet/roslyn/issues/42742")]
+ public Task ScheduleTaskInProgress(Func> operation, CancellationToken cancellationToken)
+ {
+ lock (_gate)
+ {
+ var task = _latestTask.SafeContinueWithFromAsync(_ => operation(), cancellationToken, TaskContinuationOptions.None, Scheduler);
+ _latestTask = task;
+ return task;
+ }
+ }
+#pragma warning restore
+ }
+}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/ITaskSchedulerProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/ITaskSchedulerProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e18d21b8f73b0cc4cb057861573c53044e92f170
--- /dev/null
+++ b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/ITaskSchedulerProvider.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using System.Threading.Tasks;
+
+namespace Microsoft.CodeAnalysis.Host
+{
+ ///
+ /// A factory that creates either sequential or parallel task schedulers.
+ ///
+ internal interface ITaskSchedulerProvider : IWorkspaceService
+ {
+ TaskScheduler CurrentContextScheduler { get; }
+ }
+}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceAsynchronousOperationListenerProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceAsynchronousOperationListenerProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..38fbd9c6c81b833687bc567986dd762c3aaa6d42
--- /dev/null
+++ b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceAsynchronousOperationListenerProvider.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using Microsoft.CodeAnalysis.Shared.TestHooks;
+
+namespace Microsoft.CodeAnalysis.Host
+{
+ ///
+ /// Workspace service that provides instance.
+ ///
+ internal interface IWorkspaceAsynchronousOperationListenerProvider : IWorkspaceService
+ {
+ IAsynchronousOperationListener GetListener();
+ }
+}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskScheduler.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskScheduler.cs
deleted file mode 100644
index f12e619e6f62057affee49f4aa2d8666266681b9..0000000000000000000000000000000000000000
--- a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskScheduler.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.CodeAnalysis.Host
-{
- ///
- /// An abstraction for running tasks either in sequence or in parallel.
- ///
- internal interface IWorkspaceTaskScheduler
- {
- ///
- /// Execute the task action on a thread owned by a task scheduler.
- ///
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- Task ScheduleTask(Action taskAction, string taskName, CancellationToken cancellationToken = default);
-
- ///
- /// Execute the task function on a thread owned by a task scheduler and return the schedule
- /// task that can be used to wait for the result.
- ///
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken = default);
-
- ///
- /// Execute the task function on a thread owned by a task scheduler and return the schedule
- /// task that can be used to wait for the result.
- ///
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken = default);
-
- ///
- /// Execute the task function on a thread owned by a task scheduler and return the schedule
- /// task that can be used to wait for the result.
- ///
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- Task ScheduleTask(Func> taskFunc, string taskName, CancellationToken cancellationToken = default);
- }
-}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskSchedulerFactory.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskSchedulerFactory.cs
deleted file mode 100644
index b0087ee25df77c17dd033a758dc26cb3ae39c5a4..0000000000000000000000000000000000000000
--- a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/IWorkspaceTaskSchedulerFactory.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Threading.Tasks;
-
-namespace Microsoft.CodeAnalysis.Host
-{
- ///
- /// A factory that creates either sequential or parallel task schedulers.
- ///
- internal interface IWorkspaceTaskSchedulerFactory : IWorkspaceService
- {
- ///
- /// Creates a workspace task scheduler that schedules tasks to run in parallel on the background.
- ///
- IWorkspaceTaskScheduler CreateBackgroundTaskScheduler();
-
- ///
- /// Creates a workspace task scheduler that schedules task to run in sequence to be used for raising
- /// workspace events.
- ///
- IWorkspaceTaskScheduler CreateEventingTaskQueue();
- }
-}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/TaskSchedulerProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/TaskSchedulerProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1c6562932eb476aee72d09f64e9f6aaac729b6ab
--- /dev/null
+++ b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/TaskSchedulerProvider.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using Microsoft.CodeAnalysis.Host.Mef;
+using System;
+using System.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.CodeAnalysis.Host
+{
+ [ExportWorkspaceService(typeof(ITaskSchedulerProvider), ServiceLayer.Default)]
+ [Shared]
+ internal sealed class TaskSchedulerProvider : ITaskSchedulerProvider
+ {
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public TaskSchedulerProvider()
+ {
+ }
+
+ public TaskScheduler CurrentContextScheduler
+ => (SynchronizationContext.Current != null) ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Default;
+ }
+}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceAsynchronousOperationListenerProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceAsynchronousOperationListenerProvider.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aa296bf4e0554ca9a148df0bcacb7bbe0ce37f00
--- /dev/null
+++ b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceAsynchronousOperationListenerProvider.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.TestHooks;
+using System;
+using System.Composition;
+
+namespace Microsoft.CodeAnalysis.Host
+{
+ [ExportWorkspaceService(typeof(IWorkspaceAsynchronousOperationListenerProvider), ServiceLayer.Default)]
+ [Shared]
+ internal sealed class WorkspaceAsynchronousOperationListenerProvider : IWorkspaceAsynchronousOperationListenerProvider
+ {
+ private readonly IAsynchronousOperationListener _listener;
+
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public WorkspaceAsynchronousOperationListenerProvider(IAsynchronousOperationListenerProvider listenerProvider)
+ {
+ _listener = listenerProvider.GetListener(FeatureAttribute.Workspace);
+ }
+
+ public IAsynchronousOperationListener GetListener()
+ => _listener;
+ }
+}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskQueue.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskQueue.cs
deleted file mode 100644
index 231e62af0159bc0c0557174bc0e7a8c7f9d5a39b..0000000000000000000000000000000000000000
--- a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskQueue.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.Host
-{
- internal partial class WorkspaceTaskSchedulerFactory
- {
- protected sealed class WorkspaceTaskQueue : IWorkspaceTaskScheduler
- {
- private readonly WorkspaceTaskSchedulerFactory _factory;
- private readonly SimpleTaskQueue _queue;
-
- public WorkspaceTaskQueue(WorkspaceTaskSchedulerFactory factory, TaskScheduler taskScheduler)
- {
- _factory = factory;
- _queue = new SimpleTaskQueue(taskScheduler);
- }
-
- private T3 ScheduleTask(Func taskScheduler, string taskName, T1 arg1, T2 arg2) where T3 : Task
- {
- taskName ??= GetType().Name + ".Task";
- var asyncToken = _factory.BeginAsyncOperation(taskName);
-
- var task = taskScheduler(arg1, arg2);
-
- _factory.CompleteAsyncOperation(asyncToken, task);
- return task;
- }
-
- public Task ScheduleTask(Action taskAction, string taskName, CancellationToken cancellationToken)
- {
- return ScheduleTask((t, c) => _queue.ScheduleTask(t, c), taskName, taskAction, cancellationToken);
- }
-
- public Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken)
- {
- return ScheduleTask((t, c) => _queue.ScheduleTask(t, c), taskName, taskFunc, cancellationToken);
- }
-
- public Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken = default)
- {
- return ScheduleTask((t, c) => _queue.ScheduleTask(t, c), taskName, taskFunc, cancellationToken);
- }
-
- public Task ScheduleTask(Func> taskFunc, string taskName, CancellationToken cancellationToken = default)
- {
- return ScheduleTask((t, c) => _queue.ScheduleTask(t, c), taskName, taskFunc, cancellationToken);
- }
- }
- }
-}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskScheduler.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskScheduler.cs
deleted file mode 100644
index 6f276d985b575ae253df1df1a428727d98c1cdc7..0000000000000000000000000000000000000000
--- a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.WorkspaceTaskScheduler.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.Host
-{
- internal partial class WorkspaceTaskSchedulerFactory
- {
- private class WorkspaceTaskScheduler : IWorkspaceTaskScheduler
- {
- private readonly WorkspaceTaskSchedulerFactory _factory;
- private readonly TaskScheduler _taskScheduler;
-
- public WorkspaceTaskScheduler(WorkspaceTaskSchedulerFactory factory, TaskScheduler taskScheduler)
- {
- _factory = factory;
- _taskScheduler = taskScheduler;
- }
-
- private TTask ScheduleTaskWorker(
- string taskName, Func taskCreator)
- where TTask : Task
- {
- taskName ??= GetType().Name + ".ScheduleTask";
- var asyncToken = _factory.BeginAsyncOperation(taskName);
-
- var task = taskCreator();
-
- _factory.CompleteAsyncOperation(asyncToken, task);
- return task;
- }
-
- public Task ScheduleTask(Action taskAction, string taskName, CancellationToken cancellationToken)
- {
- return ScheduleTaskWorker(
- taskName, () => Task.Factory.SafeStartNew(taskAction, cancellationToken, _taskScheduler));
- }
-
- public Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken)
- {
- return ScheduleTaskWorker(
- taskName, () => Task.Factory.SafeStartNew(taskFunc, cancellationToken, _taskScheduler));
- }
-
- public Task ScheduleTask(Func taskFunc, string taskName, CancellationToken cancellationToken = default)
- {
- return ScheduleTaskWorker(
- taskName, () => Task.Factory.SafeStartNewFromAsync(taskFunc, cancellationToken, _taskScheduler));
- }
-
- public Task ScheduleTask(Func> taskFunc, string taskName, CancellationToken cancellationToken = default)
- {
- return ScheduleTaskWorker(
- taskName, () => Task.Factory.SafeStartNewFromAsync(taskFunc, cancellationToken, _taskScheduler));
- }
- }
- }
-}
diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.cs b/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.cs
deleted file mode 100644
index bc1631af0936094dab9f025b1346f5afd83da26d..0000000000000000000000000000000000000000
--- a/src/Workspaces/Core/Portable/Workspace/Host/TaskScheduler/WorkspaceTaskSchedulerFactory.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CodeAnalysis.Host.Mef;
-using System.Composition;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.CodeAnalysis.Host
-{
- [ExportWorkspaceService(typeof(IWorkspaceTaskSchedulerFactory), ServiceLayer.Default)]
- [Shared]
- internal partial class WorkspaceTaskSchedulerFactory : IWorkspaceTaskSchedulerFactory
- {
- [ImportingConstructor]
- public WorkspaceTaskSchedulerFactory()
- {
- }
-
- public virtual IWorkspaceTaskScheduler CreateBackgroundTaskScheduler()
- {
- return new WorkspaceTaskScheduler(this, TaskScheduler.Default);
- }
-
- public virtual IWorkspaceTaskScheduler CreateEventingTaskQueue()
- {
- var taskScheduler = (SynchronizationContext.Current != null)
- ? TaskScheduler.FromCurrentSynchronizationContext()
- : TaskScheduler.Default;
-
- return new WorkspaceTaskQueue(this, taskScheduler);
- }
-
- protected virtual object BeginAsyncOperation(string taskName)
- {
- // do nothing ... overridden by services layer
- return null;
- }
-
- protected virtual void CompleteAsyncOperation(object asyncToken, Task task)
- {
- // do nothing ... overridden by services layer
- }
- }
-}
diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs
index 39c0d553264c060cac9bafa1440ada5e3b28cdc9..b28ec9dab0a94b7fe16c7c13ee223f84cf4be308 100644
--- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs
+++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs
@@ -51,7 +51,7 @@ public abstract partial class Workspace : IDisposable
// Current solution.
private Solution _latestSolution;
- private readonly IWorkspaceTaskScheduler _taskQueue;
+ private readonly TaskQueue _taskQueue;
// test hooks.
internal static bool TestHookStandaloneProjectsDoNotHoldReferences = false;
@@ -83,8 +83,9 @@ protected Workspace(HostServices host, string? workspaceKind)
_optionService.RegisterWorkspace(this);
// queue used for sending events
- var workspaceTaskSchedulerFactory = _services.GetRequiredService();
- _taskQueue = workspaceTaskSchedulerFactory.CreateEventingTaskQueue();
+ var schedulerProvider = _services.GetRequiredService();
+ var listenerProvider = _services.GetRequiredService();
+ _taskQueue = new TaskQueue(listenerProvider.GetListener(), schedulerProvider.CurrentContextScheduler);
// initialize with empty solution
var info = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create());
@@ -285,18 +286,18 @@ internal void UpdateCurrentSolutionOnOptionsChanged()
/// Executes an action as a background task, as part of a sequential queue of tasks.
///
[SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- protected internal Task ScheduleTask(Action action, string taskName = "Workspace.Task")
+ protected internal Task ScheduleTask(Action action, string? taskName = "Workspace.Task")
{
- return _taskQueue.ScheduleTask(action, taskName);
+ return _taskQueue.ScheduleTask(taskName ?? "Workspace.Task", action, CancellationToken.None);
}
///
/// Execute a function as a background task, as part of a sequential queue of tasks.
///
[SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- protected internal Task ScheduleTask(Func func, string taskName = "Workspace.Task")
+ protected internal Task ScheduleTask(Func func, string? taskName = "Workspace.Task")
{
- return _taskQueue.ScheduleTask(func, taskName);
+ return _taskQueue.ScheduleTask(taskName ?? "Workspace.Task", func, CancellationToken.None);
}
///
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
index 620bf59187db331e57d4410c3209a4adeba467a0..75f7b2a499223b931aca5c7a0ab03e3344a72538 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
@@ -401,7 +401,6 @@
-
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SimpleTaskQueue.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SimpleTaskQueue.cs
deleted file mode 100644
index 00aca0a1c2ebe625caf591a842211a2cd5210fdf..0000000000000000000000000000000000000000
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SimpleTaskQueue.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Roslyn.Utilities
-{
- ///
- /// schedules task to run in sequence.
- ///
- internal sealed class SimpleTaskQueue
- {
- private readonly TaskScheduler _taskScheduler;
-
- ///
- /// An object to synchronize reads/writes of all mutable fields of this class.
- ///
- private readonly object _gate = new object();
-
- private Task _latestTask;
-
- public SimpleTaskQueue(TaskScheduler taskScheduler)
- {
- _taskScheduler = taskScheduler;
- _latestTask = Task.CompletedTask;
- }
-
- [PerformanceSensitive(
- "https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html",
- AllowCaptures = false)]
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- public Task ScheduleTask(Action taskAction, CancellationToken cancellationToken = default)
- {
- lock (_gate)
- {
- var task = _latestTask.SafeContinueWith(_ => taskAction(), cancellationToken, TaskContinuationOptions.None, _taskScheduler);
- _latestTask = task;
- return task;
- }
- }
-
- [PerformanceSensitive(
- "https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html",
- AllowCaptures = false)]
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- public Task ScheduleTask(Func taskFunc, CancellationToken cancellationToken = default)
- {
- lock (_gate)
- {
- var task = _latestTask.SafeContinueWith(_ => taskFunc(), cancellationToken, TaskContinuationOptions.None, _taskScheduler);
- _latestTask = task;
- return task;
- }
- }
-
- [PerformanceSensitive(
- "https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html",
- AllowCaptures = false)]
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- public Task ScheduleTask(Func taskFuncAsync, CancellationToken cancellationToken = default)
- {
- lock (_gate)
- {
- var task = _latestTask.SafeContinueWithFromAsync(_ => taskFuncAsync(), cancellationToken, TaskContinuationOptions.None, _taskScheduler);
- _latestTask = task;
- return task;
- }
- }
-
- [PerformanceSensitive(
- "https://developercommunity.visualstudio.com/content/problem/854696/changing-target-framework-takes-10-minutes-with-10.html",
- AllowCaptures = false)]
- [SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
- public Task ScheduleTask(Func> taskFuncAsync, CancellationToken cancellationToken = default)
- {
- lock (_gate)
- {
- var task = _latestTask.SafeContinueWithFromAsync(_ => taskFuncAsync(), cancellationToken, TaskContinuationOptions.None, _taskScheduler);
- _latestTask = task;
- return task;
- }
- }
-
- public Task LastScheduledTask => _latestTask;
- }
-}