Skip to content

Commit

Permalink
Remove TaskQueue and SafeContinueWith extensions (#76459)
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi authored Dec 17, 2024
2 parents 1d835c2 + e5fedbb commit 71bdd3b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 282 deletions.
103 changes: 0 additions & 103 deletions src/Workspaces/Core/Portable/Utilities/TaskQueue.cs

This file was deleted.

68 changes: 55 additions & 13 deletions src/Workspaces/Core/Portable/Workspace/Workspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
Expand All @@ -19,7 +20,6 @@
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

Expand All @@ -36,6 +36,12 @@ public abstract partial class Workspace : IDisposable
{
private readonly ILegacyGlobalOptionService _legacyOptions;

private readonly IAsynchronousOperationListener _asyncOperationListener;

private readonly AsyncBatchingWorkQueue<Action> _workQueue;
private readonly CancellationTokenSource _workQueueTokenSource = new();
private readonly ITaskSchedulerProvider _taskSchedulerProvider;

// forces serialization of mutation calls from host (OnXXX methods). Must take this lock before taking stateLock.
private readonly SemaphoreSlim _serializationLock = new(initialCount: 1);

Expand All @@ -47,8 +53,6 @@ public abstract partial class Workspace : IDisposable
/// </summary>
private Solution _latestSolution;

private readonly TaskQueue _taskQueue;

// test hooks.
internal static bool TestHookStandaloneProjectsDoNotHoldReferences = false;

Expand All @@ -74,16 +78,22 @@ protected Workspace(HostServices host, string? workspaceKind)
_legacyOptions.RegisterWorkspace(this);

// queue used for sending events
var schedulerProvider = Services.GetRequiredService<ITaskSchedulerProvider>();
_taskSchedulerProvider = Services.GetRequiredService<ITaskSchedulerProvider>();

var listenerProvider = Services.GetRequiredService<IWorkspaceAsynchronousOperationListenerProvider>();
_taskQueue = new TaskQueue(listenerProvider.GetListener(), schedulerProvider.CurrentContextScheduler);
_asyncOperationListener = listenerProvider.GetListener();
_workQueue = new(
TimeSpan.Zero,
ProcessWorkQueueAsync,
_asyncOperationListener,
_workQueueTokenSource.Token);

// initialize with empty solution
var info = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create());

var emptyOptions = new SolutionOptionSet(_legacyOptions);

_latestSolution = CreateSolution(info, emptyOptions, analyzerReferences: [], fallbackAnalyzerOptions: ImmutableDictionary<string, StructuredAnalyzerConfigOptions>.Empty);
_latestSolution = CreateSolution(
SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create()),
new SolutionOptionSet(_legacyOptions),
analyzerReferences: [],
fallbackAnalyzerOptions: ImmutableDictionary<string, StructuredAnalyzerConfigOptions>.Empty);

_updateSourceGeneratorsQueue = new AsyncBatchingWorkQueue<(ProjectId? projectId, bool forceRegeneration)>(
// Idle processing speed
Expand Down Expand Up @@ -581,15 +591,25 @@ internal void UpdateCurrentSolutionOnOptionsChanged()
/// Executes an action as a background task, as part of a sequential queue of tasks.
/// </summary>
[SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
#pragma warning disable IDE0060 // Remove unused parameter
protected internal Task ScheduleTask(Action action, string? taskName = "Workspace.Task")
=> _taskQueue.ScheduleTask(taskName ?? "Workspace.Task", action, CancellationToken.None);
{
_workQueue.AddWork(action);
return _workQueue.WaitUntilCurrentBatchCompletesAsync();
}

/// <summary>
/// Execute a function as a background task, as part of a sequential queue of tasks.
/// </summary>
[SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods", Justification = "This is a Task wrapper, not an asynchronous method.")]
protected internal Task<T> ScheduleTask<T>(Func<T> func, string? taskName = "Workspace.Task")
=> _taskQueue.ScheduleTask(taskName ?? "Workspace.Task", func, CancellationToken.None);
protected internal async Task<T> ScheduleTask<T>(Func<T> func, string? taskName = "Workspace.Task")
{
T? result = default;
_workQueue.AddWork(() => result = func());
await _workQueue.WaitUntilCurrentBatchCompletesAsync().ConfigureAwait(false);
return result!;
}
#pragma warning restore IDE0060 // Remove unused parameter

/// <summary>
/// Override this method to act immediately when the text of a document has changed, as opposed
Expand Down Expand Up @@ -695,6 +715,28 @@ protected virtual void Dispose(bool finalize)

// We're disposing this workspace. Stop any work to update SG docs in the background.
_updateSourceGeneratorsQueueTokenSource.Cancel();
_workQueueTokenSource.Cancel();
}

private async ValueTask ProcessWorkQueueAsync(ImmutableSegmentedList<Action> list, CancellationToken cancellationToken)
{
// Hop over to the right scheduler to execute all this work.
await Task.Factory.StartNew(() =>
{
foreach (var item in list)
{
cancellationToken.ThrowIfCancellationRequested();

try
{
item();
}
catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e))
{
// Ensure we continue onto further items, even if one particular item fails.
}
}
}, cancellationToken, TaskCreationOptions.None, _taskSchedulerProvider.CurrentContextScheduler).ConfigureAwait(false);
}

#region Host API
Expand Down
8 changes: 4 additions & 4 deletions src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,9 @@ internal void OnSourceGeneratedDocumentOpened(

// Fire and forget that the workspace is changing.
// We raise 2 events for source document opened.
var token = _taskQueue.Listener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentOpened));
var token = _asyncOperationListener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentOpened));
_ = RaiseDocumentOpenedEventAsync(document).CompletesAsyncOperation(token);
token = _taskQueue.Listener.BeginAsyncOperation(TextDocumentOpenedEventName);
token = _asyncOperationListener.BeginAsyncOperation(TextDocumentOpenedEventName);
_ = RaiseTextDocumentOpenedEventAsync(document).CompletesAsyncOperation(token);
}

Expand All @@ -475,9 +475,9 @@ internal void OnSourceGeneratedDocumentClosed(SourceGeneratedDocument document)

// Fire and forget that the workspace is changing.
// We raise 2 events for source document closed.
var token = _taskQueue.Listener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentClosed));
var token = _asyncOperationListener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentClosed));
_ = RaiseDocumentClosedEventAsync(document).CompletesAsyncOperation(token);
token = _taskQueue.Listener.BeginAsyncOperation(TextDocumentClosedEventName);
token = _asyncOperationListener.BeginAsyncOperation(TextDocumentClosedEventName);
_ = RaiseTextDocumentClosedEventAsync(document).CompletesAsyncOperation(token);
}
}
Expand Down
Loading

0 comments on commit 71bdd3b

Please sign in to comment.