зеркало из https://github.com/dotnet/razor.git
Fix initialization contract for RazorProjectInfo drivers
`AbstractRazorProjectInfoDriver`can't call `InitializeAsync(...)` in its constructor because the driver will only be partially constructed. To address that, add a `StartInitialization` method that drivers call from their constructor. This will kick off initialization and set the result of a `TaskCompletionSource` when it finishes.
This commit is contained in:
Родитель
af6e0d8735
Коммит
8e085c4e66
|
@ -57,6 +57,8 @@ internal partial class FileWatcherBasedRazorProjectInfoDriver : AbstractRazorPro
|
|||
_fileSystemWatcher?.Dispose();
|
||||
_fileSystemWatcher = null;
|
||||
});
|
||||
|
||||
StartInitialization();
|
||||
}
|
||||
|
||||
protected override async Task InitializeAsync(CancellationToken cancellationToken)
|
||||
|
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Razor.PooledObjects;
|
|||
using Microsoft.AspNetCore.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Utilities;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||
|
||||
|
@ -28,7 +29,7 @@ internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectIn
|
|||
private readonly AsyncBatchingWorkQueue<Work> _workQueue;
|
||||
private readonly Dictionary<ProjectKey, RazorProjectInfo> _latestProjectInfoMap;
|
||||
private ImmutableArray<IRazorProjectInfoListener> _listeners;
|
||||
private readonly Task _initializeTask;
|
||||
private readonly TaskCompletionSource<bool> _initializationTaskSource;
|
||||
|
||||
protected CancellationToken DisposalToken => _disposeTokenSource.Token;
|
||||
|
||||
|
@ -40,7 +41,7 @@ internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectIn
|
|||
_workQueue = new AsyncBatchingWorkQueue<Work>(delay ?? DefaultDelay, ProcessBatchAsync, _disposeTokenSource.Token);
|
||||
_latestProjectInfoMap = [];
|
||||
_listeners = [];
|
||||
_initializeTask = InitializeAsync(_disposeTokenSource.Token);
|
||||
_initializationTaskSource = new();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -57,10 +58,29 @@ internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectIn
|
|||
public Task WaitForInitializationAsync()
|
||||
{
|
||||
#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks
|
||||
return _initializeTask;
|
||||
return _initializationTaskSource.Task;
|
||||
#pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MUST be called in the constructor of any <see cref="AbstractRazorProjectInfoDriver"/> descendent
|
||||
/// to kick off initialization.
|
||||
/// </summary>
|
||||
protected void StartInitialization()
|
||||
{
|
||||
// Kick off initialization asynchronously and call TrySetResult(true) in the continuation.
|
||||
InitializeAsync(_disposeTokenSource.Token)
|
||||
.ContinueWith(
|
||||
_ =>
|
||||
{
|
||||
_initializationTaskSource.TrySetResult(true);
|
||||
},
|
||||
_disposeTokenSource.Token,
|
||||
TaskContinuationOptions.OnlyOnRanToCompletion,
|
||||
TaskScheduler.Default)
|
||||
.Forget();
|
||||
}
|
||||
|
||||
protected abstract Task InitializeAsync(CancellationToken cancellationToken);
|
||||
|
||||
private async ValueTask ProcessBatchAsync(ImmutableArray<Work> items, CancellationToken token)
|
||||
|
@ -125,7 +145,7 @@ internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectIn
|
|||
|
||||
public ImmutableArray<RazorProjectInfo> GetLatestProjectInfo()
|
||||
{
|
||||
if (!_initializeTask.IsCompleted)
|
||||
if (!_initializationTaskSource.Task.IsCompleted)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(GetLatestProjectInfo)} cannot be called before initialization is complete.");
|
||||
}
|
||||
|
@ -145,7 +165,7 @@ internal abstract partial class AbstractRazorProjectInfoDriver : IRazorProjectIn
|
|||
|
||||
public void AddListener(IRazorProjectInfoListener listener)
|
||||
{
|
||||
if (!_initializeTask.IsCompleted)
|
||||
if (!_initializationTaskSource.Task.IsCompleted)
|
||||
{
|
||||
throw new InvalidOperationException($"An {nameof(IRazorProjectInfoListener)} cannot be added before initialization is complete.");
|
||||
}
|
||||
|
|
|
@ -10,13 +10,19 @@ using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.ProjectSystem;
|
||||
|
||||
internal sealed partial class RazorProjectInfoDriver(
|
||||
IProjectSnapshotManager projectManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
TimeSpan? delay = null)
|
||||
: AbstractRazorProjectInfoDriver(loggerFactory, delay)
|
||||
internal sealed partial class RazorProjectInfoDriver : AbstractRazorProjectInfoDriver
|
||||
{
|
||||
private readonly IProjectSnapshotManager _projectManager = projectManager;
|
||||
private readonly IProjectSnapshotManager _projectManager;
|
||||
|
||||
public RazorProjectInfoDriver(
|
||||
IProjectSnapshotManager projectManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
TimeSpan? delay = null) : base(loggerFactory, delay)
|
||||
{
|
||||
_projectManager = projectManager;
|
||||
|
||||
StartInitialization();
|
||||
}
|
||||
|
||||
protected override Task InitializeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче