Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 21 additions & 22 deletions src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ protected override void RegisterInitializeAsyncWork(PackageLoadTasks packageInit
base.RegisterInitializeAsyncWork(packageInitializationTasks);

packageInitializationTasks.AddTask(isMainThreadTask: true, task: PackageInitializationMainThreadAsync);
packageInitializationTasks.AddTask(isMainThreadTask: false, task: PackageInitializationBackgroundThreadAsync);
}

private async Task PackageInitializationMainThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
Expand All @@ -58,35 +59,33 @@ private async Task PackageInitializationMainThreadAsync(PackageLoadTasks package
RegisterEditorFactory(editorFactory);
}

// Misc workspace has to be up and running by the time our package is usable so that it can track running
// doc events and appropriately map files to/from it and other relevant workspaces (like the
// metadata-as-source workspace).
var miscellaneousFilesWorkspace = this.ComponentModel.GetService<MiscellaneousFilesWorkspace>();

// awaiting an IVsTask guarantees to return on the captured context
await shell.LoadPackageAsync(Guids.RoslynPackageId);
}

packageInitializationTasks.AddTask(
isMainThreadTask: false,
task: (PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken) =>
{
RegisterLanguageService(typeof(TLanguageService), async cancellationToken =>
{
// Ensure we're on the BG when creating the language service.
await TaskScheduler.Default;
private Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken)
{
RegisterLanguageService(typeof(TLanguageService), async cancellationToken =>
{
// Ensure we're on the BG when creating the language service.
await TaskScheduler.Default;

// Create the language service, tell it to set itself up, then store it in a field
// so we can notify it that it's time to clean up.
_languageService = CreateLanguageService();
await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false);

// Create the language service, tell it to set itself up, then store it in a field
// so we can notify it that it's time to clean up.
_languageService = CreateLanguageService();
await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false);
return _languageService.ComAggregate!;
});

return _languageService.ComAggregate!;
});
// Misc workspace has to be up and running by the time our package is usable so that it can track running
// doc events and appropriately map files to/from it and other relevant workspaces (like the
// metadata-as-source workspace).
var miscellaneousFilesWorkspace = this.ComponentModel.GetService<MiscellaneousFilesWorkspace>();

RegisterMiscellaneousFilesWorkspaceInformation(miscellaneousFilesWorkspace);
RegisterMiscellaneousFilesWorkspaceInformation(miscellaneousFilesWorkspace);

return Task.CompletedTask;
});
return Task.CompletedTask;
}

protected override void RegisterOnAfterPackageLoadedAsyncWork(PackageLoadTasks afterPackageLoadedTasks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IOpenText
private readonly IThreadingContext _threadingContext;
private readonly IVsService<IVsTextManager> _textManagerService;
private readonly OpenTextBufferProvider _openTextBufferProvider;
private readonly IMetadataAsSourceFileService _fileTrackingMetadataAsSourceService;
private readonly Lazy<IMetadataAsSourceFileService> _fileTrackingMetadataAsSourceService;

private readonly ConcurrentDictionary<Guid, LanguageInformation> _languageInformationByLanguageGuid = [];

Expand All @@ -47,24 +47,24 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IOpenText
/// </summary>
private readonly Dictionary<string, (ProjectId projectId, SourceTextContainer textContainer)> _monikersToProjectIdAndContainer = [];

private readonly ImmutableArray<MetadataReference> _metadataReferences;
private readonly Lazy<ImmutableArray<MetadataReference>> _metadataReferences;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public MiscellaneousFilesWorkspace(
IThreadingContext threadingContext,
IVsService<SVsTextManager, IVsTextManager> textManagerService,
OpenTextBufferProvider openTextBufferProvider,
IMetadataAsSourceFileService fileTrackingMetadataAsSourceService,
VisualStudioWorkspace visualStudioWorkspace)
: base(visualStudioWorkspace.Services.HostServices, WorkspaceKind.MiscellaneousFiles)
Lazy<IMetadataAsSourceFileService> fileTrackingMetadataAsSourceService,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why making the IMetadataAsSourceFileService lazy? Was there some other hidden UI thread dependency there, or it's just a generally big service and there's no reason to ask for it early? A comment might be good either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to flip that question and rather us question why parameters aren't lazy in the package load path. This one switched over to being lazy pretty easily, so I did.

Composition.ExportProvider exportProvider)
: base(VisualStudioMefHostServices.Create(exportProvider), WorkspaceKind.MiscellaneousFiles)
{
_threadingContext = threadingContext;
_textManagerService = textManagerService;
_openTextBufferProvider = openTextBufferProvider;
_fileTrackingMetadataAsSourceService = fileTrackingMetadataAsSourceService;

_metadataReferences = [.. CreateMetadataReferences()];
_metadataReferences = new(() => [.. CreateMetadataReferences()]);

_openTextBufferProvider.AddListener(this);
}
Expand Down Expand Up @@ -122,6 +122,10 @@ public void RegisterLanguage(Guid languageGuid, string languageName, string scri

private IEnumerable<MetadataReference> CreateMetadataReferences()
{
// VisualStudioMetadataReferenceManager construction requires the main thread
// TODO: Determine if main thread affinity can be removed: https://github.com/dotnet/roslyn/issues/77791
_threadingContext.ThrowIfNotOnUIThread();

var manager = this.Services.GetService<VisualStudioMetadataReferenceManager>();
var searchPaths = VisualStudioMetadataReferenceManager.GetReferencePaths();

Expand Down Expand Up @@ -261,7 +265,7 @@ private void AttachToDocument(string moniker, ITextBuffer textBuffer)
{
_threadingContext.ThrowIfNotOnUIThread();

if (_fileTrackingMetadataAsSourceService.TryAddDocumentToWorkspace(moniker, textBuffer.AsTextContainer(), out var _))
if (_fileTrackingMetadataAsSourceService.Value.TryAddDocumentToWorkspace(moniker, textBuffer.AsTextContainer(), out var _))
{
// We already added it, so we will keep it excluded from the misc files workspace
return;
Expand All @@ -282,20 +286,24 @@ private void AttachToDocument(string moniker, ITextBuffer textBuffer)
/// </summary>
private ProjectInfo CreateProjectInfoForDocument(string filePath)
{
// Potential calculation of _metadataReferences requires being on the main thread
// TODO: Determine if main thread affinity can be removed: https://github.com/dotnet/roslyn/issues/77791
_threadingContext.ThrowIfNotOnUIThread();

// This should always succeed since we only got here if we already confirmed the moniker is acceptable
var languageInformation = TryGetLanguageInformation(filePath);
Contract.ThrowIfNull(languageInformation);

var checksumAlgorithm = SourceHashAlgorithms.Default;
var fileLoader = new WorkspaceFileTextLoader(Services.SolutionServices, filePath, defaultEncoding: null);
return MiscellaneousFileUtilities.CreateMiscellaneousProjectInfoForDocument(
this, filePath, fileLoader, languageInformation, checksumAlgorithm, Services.SolutionServices, _metadataReferences);
this, filePath, fileLoader, languageInformation, checksumAlgorithm, Services.SolutionServices, _metadataReferences.Value);
}

private void DetachFromDocument(string moniker)
{
_threadingContext.ThrowIfNotOnUIThread();
if (_fileTrackingMetadataAsSourceService.TryRemoveDocumentFromWorkspace(moniker))
if (_fileTrackingMetadataAsSourceService.Value.TryRemoveDocumentFromWorkspace(moniker))
{
return;
}
Expand Down
Loading