Don't get IVsAsyncFileChangeEx on background thread

`VisualStudioFileChangeTrackerFactory` retrieves an `IVsAsyncFileChangeEx` service in its constructor. Because this is an `[ImportingConstructor]` it will likely be called on the thread pool and implicitly marshal to the UI thread to perform a QI as part of retrieving the service. This can cause a hang if the UI thread is tied up with other work, such as when a solution is loaded.

This change avoids retrieving `IVsAsyncFileChangeEx` until its needed. This does block on retrieving the service. However, avoiding that will require a more substantial change that I'll follow up with on `main`.
This commit is contained in:
Dustin Campbell 2024-03-26 11:01:02 -07:00
Родитель 18e4175ac5
Коммит 9e109f88a7
1 изменённых файлов: 17 добавлений и 7 удалений

Просмотреть файл

@ -13,10 +13,10 @@ namespace Microsoft.VisualStudio.Editor.Razor.Documents;
[Export(typeof(IFileChangeTrackerFactory))]
internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
{
private readonly IErrorReporter _errorReporter;
private readonly IVsAsyncFileChangeEx _fileChangeService;
private readonly ProjectSnapshotManagerDispatcher _projectSnapshotManagerDispatcher;
private readonly ProjectSnapshotManagerDispatcher _dispatcher;
private readonly JoinableTaskContext _joinableTaskContext;
private readonly IErrorReporter _errorReporter;
private readonly JoinableTask<IVsAsyncFileChangeEx> _getFileChangeServiceTask;
[ImportingConstructor]
public VisualStudioFileChangeTrackerFactory(
@ -25,10 +25,17 @@ internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
ProjectSnapshotManagerDispatcher dispatcher,
IErrorReporter errorReporter)
{
_errorReporter = errorReporter;
_fileChangeService = (IVsAsyncFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx));
_projectSnapshotManagerDispatcher = dispatcher;
_dispatcher = dispatcher;
_joinableTaskContext = joinableTaskContext;
_errorReporter = errorReporter;
var jtf = _joinableTaskContext.Factory;
_getFileChangeServiceTask = jtf.RunAsync(async () =>
{
await jtf.SwitchToMainThreadAsync();
return (IVsAsyncFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx));
});
}
public IFileChangeTracker Create(string filePath)
@ -38,6 +45,9 @@ internal class VisualStudioFileChangeTrackerFactory : IFileChangeTrackerFactory
throw new ArgumentException(SR.ArgumentCannotBeNullOrEmpty, nameof(filePath));
}
return new VisualStudioFileChangeTracker(filePath, _errorReporter, _fileChangeService, _projectSnapshotManagerDispatcher, _joinableTaskContext);
// TODO: Make IFileChangeTrackerFactory.Create(...) asynchronous to avoid blocking here.
var fileChangeService = _getFileChangeServiceTask.Join();
return new VisualStudioFileChangeTracker(filePath, _errorReporter, fileChangeService, _dispatcher, _joinableTaskContext);
}
}