Skip to content

Commit fe4bb77

Browse files
Ensure we pass unique binlog paths to each BuildHost
Otherwise if we launch multiple proceses, they might step atop each other and cause locking issues.
1 parent 0eb510a commit fe4bb77

File tree

8 files changed

+42
-23
lines changed

8 files changed

+42
-23
lines changed

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/BinlogNamer.cs renamed to src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/BinLogPathProvider.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
using System.Composition;
66
using Microsoft.CodeAnalysis.Host.Mef;
7+
using Microsoft.CodeAnalysis.MSBuild;
78
using Microsoft.CodeAnalysis.Options;
89
using Microsoft.Extensions.Logging;
910
using Microsoft.VisualStudio.Composition;
1011

1112
namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;
1213

13-
[Export(typeof(BinlogNamer)), Shared]
14-
internal sealed class BinlogNamer
14+
[Export(typeof(IBinLogPathProvider)), Shared]
15+
internal sealed class BinLogPathProvider : IBinLogPathProvider
1516
{
1617
/// <summary>
1718
/// The suffix to use for the binary log name; incremented each time we have a new build. Should be incremented with <see cref="Interlocked.Increment(ref int)"/>.
@@ -28,13 +29,13 @@ internal sealed class BinlogNamer
2829

2930
[ImportingConstructor]
3031
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
31-
public BinlogNamer(IGlobalOptionService globalOptionService, ILoggerFactory loggerFactory)
32+
public BinLogPathProvider(IGlobalOptionService globalOptionService, ILoggerFactory loggerFactory)
3233
{
3334
_globalOptionService = globalOptionService;
34-
_logger = loggerFactory.CreateLogger<BinlogNamer>();
35+
_logger = loggerFactory.CreateLogger<BinLogPathProvider>();
3536
}
3637

37-
internal string? GetMSBuildBinaryLogPath()
38+
public string? GetNewLogPath()
3839
{
3940
if (_globalOptionService.GetOption(LanguageServerProjectSystemOptionsStorage.BinaryLogPath) is not string binaryLogDirectory)
4041
return null;

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileBasedProgramsProjectSystem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public FileBasedProgramsProjectSystem(
4141
IAsynchronousOperationListenerProvider listenerProvider,
4242
ProjectLoadTelemetryReporter projectLoadTelemetry,
4343
ServerConfigurationFactory serverConfigurationFactory,
44-
BinlogNamer binlogNamer)
44+
IBinLogPathProvider binLogPathProvider)
4545
: base(
4646
workspaceFactory.FileBasedProgramsProjectFactory,
4747
workspaceFactory.TargetFrameworkManager,
@@ -52,7 +52,7 @@ public FileBasedProgramsProjectSystem(
5252
listenerProvider,
5353
projectLoadTelemetry,
5454
serverConfigurationFactory,
55-
binlogNamer)
55+
binLogPathProvider)
5656
{
5757
_lspServices = lspServices;
5858
_logger = loggerFactory.CreateLogger<FileBasedProgramsProjectSystem>();

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileBasedProgramsWorkspaceProviderFactory.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.CodeAnalysis.LanguageServer.Handler;
99
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry;
1010
using Microsoft.CodeAnalysis.MetadataAsSource;
11+
using Microsoft.CodeAnalysis.MSBuild;
1112
using Microsoft.CodeAnalysis.Options;
1213
using Microsoft.CodeAnalysis.ProjectSystem;
1314
using Microsoft.CodeAnalysis.Shared.TestHooks;
@@ -33,10 +34,10 @@ internal sealed class FileBasedProgramsWorkspaceProviderFactory(
3334
IAsynchronousOperationListenerProvider listenerProvider,
3435
ProjectLoadTelemetryReporter projectLoadTelemetry,
3536
ServerConfigurationFactory serverConfigurationFactory,
36-
BinlogNamer binlogNamer) : ILspMiscellaneousFilesWorkspaceProviderFactory
37+
IBinLogPathProvider binLogPathProvider) : ILspMiscellaneousFilesWorkspaceProviderFactory
3738
{
3839
public ILspMiscellaneousFilesWorkspaceProvider CreateLspMiscellaneousFilesWorkspaceProvider(ILspServices lspServices, HostServices hostServices)
3940
{
40-
return new FileBasedProgramsProjectSystem(lspServices, metadataAsSourceFileService, workspaceFactory, fileChangeWatcher, globalOptionService, loggerFactory, listenerProvider, projectLoadTelemetry, serverConfigurationFactory, binlogNamer);
41+
return new FileBasedProgramsProjectSystem(lspServices, metadataAsSourceFileService, workspaceFactory, fileChangeWatcher, globalOptionService, loggerFactory, listenerProvider, projectLoadTelemetry, serverConfigurationFactory, binLogPathProvider);
4142
}
4243
}

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal abstract class LanguageServerProjectLoader
4343
protected readonly ILoggerFactory LoggerFactory;
4444
private readonly ILogger _logger;
4545
private readonly ProjectLoadTelemetryReporter _projectLoadTelemetryReporter;
46-
private readonly BinlogNamer _binlogNamer;
46+
private readonly IBinLogPathProvider _binLogPathProvider;
4747
protected readonly ImmutableDictionary<string, string> AdditionalProperties;
4848

4949
/// <summary>
@@ -98,7 +98,7 @@ protected LanguageServerProjectLoader(
9898
IAsynchronousOperationListenerProvider listenerProvider,
9999
ProjectLoadTelemetryReporter projectLoadTelemetry,
100100
ServerConfigurationFactory serverConfigurationFactory,
101-
BinlogNamer binlogNamer)
101+
IBinLogPathProvider binLogPathProvider)
102102
{
103103
ProjectFactory = projectFactory;
104104
_targetFrameworkManager = targetFrameworkManager;
@@ -108,7 +108,7 @@ protected LanguageServerProjectLoader(
108108
LoggerFactory = loggerFactory;
109109
_logger = loggerFactory.CreateLogger(nameof(LanguageServerProjectLoader));
110110
_projectLoadTelemetryReporter = projectLoadTelemetry;
111-
_binlogNamer = binlogNamer;
111+
_binLogPathProvider = binLogPathProvider;
112112
var workspace = projectFactory.Workspace;
113113
var razorDesignTimePath = serverConfigurationFactory.ServerConfiguration?.RazorDesignTimePath;
114114

@@ -145,9 +145,7 @@ private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad
145145

146146
// TODO: support configuration switching
147147

148-
var binaryLogPath = _binlogNamer.GetMSBuildBinaryLogPath();
149-
150-
await using var buildHostProcessManager = new BuildHostProcessManager(globalMSBuildProperties: AdditionalProperties, binaryLogPath: binaryLogPath, loggerFactory: LoggerFactory);
148+
await using var buildHostProcessManager = new BuildHostProcessManager(globalMSBuildProperties: AdditionalProperties, binaryLogPathProvider: _binLogPathProvider, loggerFactory: LoggerFactory);
151149
var toastErrorReporter = new ToastErrorReporter();
152150

153151
try

src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public LanguageServerProjectSystem(
3434
IAsynchronousOperationListenerProvider listenerProvider,
3535
ProjectLoadTelemetryReporter projectLoadTelemetry,
3636
ServerConfigurationFactory serverConfigurationFactory,
37-
BinlogNamer binlogNamer)
37+
IBinLogPathProvider binLogPathProvider)
3838
: base(
3939
workspaceFactory.HostProjectFactory,
4040
workspaceFactory.TargetFrameworkManager,
@@ -45,7 +45,7 @@ public LanguageServerProjectSystem(
4545
listenerProvider,
4646
projectLoadTelemetry,
4747
serverConfigurationFactory,
48-
binlogNamer)
48+
binLogPathProvider)
4949
{
5050
_logger = loggerFactory.CreateLogger(nameof(LanguageServerProjectSystem));
5151
var workspace = ProjectFactory.Workspace;

src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ internal sealed class BuildHostProcessManager : IAsyncDisposable
2525
private readonly ImmutableDictionary<string, string> _globalMSBuildProperties;
2626
private readonly ILoggerFactory? _loggerFactory;
2727
private readonly ILogger? _logger;
28-
private readonly string? _binaryLogPath;
28+
private readonly IBinLogPathProvider? _binaryLogPathProvider;
2929

3030
private readonly SemaphoreSlim _gate = new(initialCount: 1);
3131
private readonly Dictionary<BuildHostProcessKind, BuildHostProcess> _processes = [];
3232

33-
public BuildHostProcessManager(ImmutableDictionary<string, string>? globalMSBuildProperties = null, string? binaryLogPath = null, ILoggerFactory? loggerFactory = null)
33+
public BuildHostProcessManager(ImmutableDictionary<string, string>? globalMSBuildProperties = null, IBinLogPathProvider? binaryLogPathProvider = null, ILoggerFactory? loggerFactory = null)
3434
{
3535
_globalMSBuildProperties = globalMSBuildProperties ?? ImmutableDictionary<string, string>.Empty;
36-
_binaryLogPath = binaryLogPath;
36+
_binaryLogPathProvider = binaryLogPathProvider;
3737
_loggerFactory = loggerFactory;
3838
_logger = loggerFactory?.CreateLogger<BuildHostProcessManager>();
3939
}
@@ -244,10 +244,10 @@ private void AppendBuildHostCommandLineArgumentsConfigureProcess(ProcessStartInf
244244
AddArgument(processStartInfo, globalMSBuildProperty.Key + '=' + globalMSBuildProperty.Value);
245245
}
246246

247-
if (_binaryLogPath is not null)
247+
if (_binaryLogPathProvider?.GetNewLogPath() is string binaryLogPath)
248248
{
249249
AddArgument(processStartInfo, "--binlog");
250-
AddArgument(processStartInfo, _binaryLogPath);
250+
AddArgument(processStartInfo, binaryLogPath);
251251
}
252252

253253
AddArgument(processStartInfo, "--locale");
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace Microsoft.CodeAnalysis.MSBuild;
6+
7+
internal interface IBinLogPathProvider
8+
{
9+
/// <summary>
10+
/// Returns a new log path. Each call will return a new name, so that way we don't have collisions if multiple processes are writing to different logs.
11+
/// </summary>
12+
/// <returns>A new path, or null if no logging is currently wanted. An instance is allowed to switch between return null and returning non-null if
13+
/// the user changes configuration.</returns>
14+
string? GetNewLogPath();
15+
}

src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Collections.Generic;
66
using System.Collections.Immutable;
7+
using Moq;
78
using Roslyn.Test.Utilities;
89
using Xunit;
910

@@ -65,7 +66,10 @@ internal void ProcessStartInfo_PassesBinLogPath(BuildHostProcessKind buildHostKi
6566
{
6667
const string BinaryLogPath = "test.binlog";
6768

68-
var processStartInfo = new BuildHostProcessManager(binaryLogPath: BinaryLogPath)
69+
var binLogPathProviderMock = new Mock<IBinLogPathProvider>(MockBehavior.Strict);
70+
binLogPathProviderMock.Setup(m => m.GetNewLogPath()).Returns(BinaryLogPath);
71+
72+
var processStartInfo = new BuildHostProcessManager(binaryLogPathProvider: binLogPathProviderMock.Object)
6973
.CreateBuildHostStartInfo(buildHostKind, pipeName: "");
7074

7175
#if NET

0 commit comments

Comments
 (0)