Skip to content

Commit

Permalink
Lazy load ISourceLinkService to reduce DLL loads (#58108)
Browse files Browse the repository at this point in the history
Avoid loading debug binaries in scenarios where debugging and sourcelink are not used.
  • Loading branch information
ryzngard authored Dec 6, 2021
1 parent f17419c commit a3a6230
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.Immutable;
using System.IO;
using System.Security.Cryptography;
Expand Down Expand Up @@ -36,7 +37,7 @@ await RunTestAsync(async path =>
var sourceFilePath = Path.Combine(path, "SourceLink.cs");
File.Move(GetSourceFilePath(path), sourceFilePath);

var sourceLinkService = new TestSourceLinkService(sourceFilePath: sourceFilePath);
var sourceLinkService = new Lazy<ISourceLinkService?>(() => new TestSourceLinkService(sourceFilePath: sourceFilePath));
var service = new PdbSourceDocumentLoaderService(sourceLinkService);

using var hash = SHA256.Create();
Expand Down Expand Up @@ -69,7 +70,7 @@ await RunTestAsync(async path =>
var sourceFilePath = Path.Combine(path, "SourceLink.cs");
File.Move(GetSourceFilePath(path), sourceFilePath);

var sourceLinkService = new TestSourceLinkService(sourceFilePath: sourceFilePath);
var sourceLinkService = new Lazy<ISourceLinkService?>(() => new TestSourceLinkService(sourceFilePath: sourceFilePath));
var service = new PdbSourceDocumentLoaderService(sourceLinkService);

var sourceDocument = new SourceDocument("goo.cs", Text.SourceHashAlgorithm.None, default, null, SourceLinkUrl: null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ internal class MetadataAsSourceFileService : IMetadataAsSourceFileService
private Mutex? _mutex;
private string? _rootTemporaryPathWithGuid;
private readonly string _rootTemporaryPath;
private readonly ImmutableArray<IMetadataAsSourceFileProvider> _providers;
private readonly ImmutableArray<Lazy<IMetadataAsSourceFileProvider, MetadataAsSourceFileProviderMetadata>> _providers;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public MetadataAsSourceFileService([ImportMany] IEnumerable<Lazy<IMetadataAsSourceFileProvider, MetadataAsSourceFileProviderMetadata>> providers)
{
_providers = ExtensionOrderer.Order(providers).Select(lz => lz.Value).ToImmutableArray();
_providers = ExtensionOrderer.Order(providers).ToImmutableArray();
_rootTemporaryPath = Path.Combine(Path.GetTempPath(), "MetadataAsSource");
}

Expand Down Expand Up @@ -90,8 +90,9 @@ public async Task<MetadataAsSourceFile> GetGeneratedFileAsync(Project project, I
Contract.ThrowIfNull(_workspace);
var tempPath = GetRootPathWithGuid_NoLock();

foreach (var provider in _providers)
foreach (var lazyProvider in _providers)
{
var provider = lazyProvider.Value;
var providerTempPath = Path.Combine(tempPath, provider.GetType().Name);
var result = await provider.GetGeneratedFileAsync(_workspace, project, symbol, signaturesOnly, allowDecompilation, providerTempPath, cancellationToken).ConfigureAwait(false);
if (result is not null)
Expand Down Expand Up @@ -179,7 +180,9 @@ public void CleanupGeneratedFiles()
_rootTemporaryPathWithGuid = null;
}

foreach (var provider in _providers)
// Only cleanup for providers that have actually generated a file. This keeps us from
// accidentally loading lazy providers on cleanup that weren't used
foreach (var provider in _tempFileToProviderMap.Values.Distinct())
{
provider.CleanupGeneratedFiles(_workspace);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ namespace Microsoft.CodeAnalysis.PdbSourceDocument
internal sealed class PdbSourceDocumentLoaderService : IPdbSourceDocumentLoaderService
{
private const int SourceLinkTimeout = 1000;
private readonly ISourceLinkService? _sourceLinkService;

/// <summary>
/// Lazy import ISourceLinkService because it can cause debugger
/// binaries to be eagerly loaded even if they are never used.
/// </summary>
private readonly Lazy<ISourceLinkService?> _sourceLinkService;

[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code")]
public PdbSourceDocumentLoaderService([Import(AllowDefault = true)] ISourceLinkService? sourceLinkService)
public PdbSourceDocumentLoaderService([Import(AllowDefault = true)] Lazy<ISourceLinkService?> sourceLinkService)
{
_sourceLinkService = sourceLinkService;
}
Expand Down Expand Up @@ -105,14 +110,14 @@ public PdbSourceDocumentLoaderService([Import(AllowDefault = true)] ISourceLinkS

private async Task<SourceFileInfo?> TryGetSourceLinkFileAsync(SourceDocument sourceDocument, Encoding encoding, IPdbSourceDocumentLogger? logger, CancellationToken cancellationToken)
{
if (_sourceLinkService is null || sourceDocument.SourceLinkUrl is null)
if (sourceDocument.SourceLinkUrl is null || _sourceLinkService.Value is null)
return null;

// This should ideally be the repo-relative path to the file, and come from SourceLink: https://github.com/dotnet/sourcelink/pull/699
var relativePath = Path.GetFileName(sourceDocument.FilePath);

var delay = Task.Delay(SourceLinkTimeout, cancellationToken);
var sourceFileTask = _sourceLinkService.GetSourceFilePathAsync(sourceDocument.SourceLinkUrl, relativePath, logger, cancellationToken);
var sourceFileTask = _sourceLinkService.Value.GetSourceFilePathAsync(sourceDocument.SourceLinkUrl, relativePath, logger, cancellationToken);

var winner = await Task.WhenAny(sourceFileTask, delay).ConfigureAwait(false);

Expand Down

0 comments on commit a3a6230

Please sign in to comment.