未验证 提交 0e63260c 编写于 作者: A Andy Gocke 提交者: GitHub

Merge pull request #34607 from agocke/use-pipe-api

Use new CoreFX API for creating NamedPipeServerStreams
......@@ -73,11 +73,6 @@ protected virtual int RunServerCore(string pipeName, IClientConnectionHost conne
TimeSpan? keepAlive = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (BuildServerConnection.IsPipePathTooLong(pipeName, tempPath))
{
return CommonCompiler.Failed;
}
keepAlive = keepAlive ?? GetKeepAliveTimeout();
listener = listener ?? new EmptyDiagnosticListener();
clientConnectionHost = clientConnectionHost ?? CreateClientConnectionHost(pipeName);
......
......@@ -16,9 +16,6 @@ namespace Microsoft.CodeAnalysis.CompilerServer
{
internal sealed class NamedPipeClientConnectionHost : IClientConnectionHost
{
// Size of the buffers to use: 64K
private const int PipeBufferSize = 0x10000;
private readonly ICompilerServerHost _compilerServerHost;
private readonly string _pipeName;
private int _loggingIdentifier;
......@@ -48,15 +45,7 @@ private async Task<NamedPipeServerStream> CreateListenTaskCore(CancellationToken
// (out of handles?), or the pipe was disconnected before we
// starting listening.
CompilerServerLogger.Log("Constructing pipe '{0}'.", _pipeName);
var pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
var pipeStream = NamedPipeUtil.CreateServer(
_pipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
pipeOptions,
PipeBufferSize,
PipeBufferSize);
var pipeStream = NamedPipeUtil.CreateServer(_pipeName);
CompilerServerLogger.Log("Successfully constructed pipe '{0}'.", _pipeName);
CompilerServerLogger.Log("Waiting for new connection");
......
......@@ -376,7 +376,7 @@ public void MutexAcquiredWhenRunningServer()
[Fact]
public async Task ShutdownRequestDirect()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var serverProcessId = await ServerUtil.SendShutdown(serverData.PipeName);
Assert.Equal(Process.GetCurrentProcess().Id, serverProcessId);
......@@ -395,7 +395,7 @@ public async Task ShutdownDoesNotAbortCompilation()
using (var startedMre = new ManualResetEvent(initialState: false))
using (var finishedMre = new ManualResetEvent(initialState: false))
using (var serverData = ServerUtil.CreateServer(compilerServerHost: host))
using (var serverData = await ServerUtil.CreateServer(compilerServerHost: host))
{
// Create a compilation that is guaranteed to complete after the shutdown is seen.
host.RunCompilation = (request, cancellationToken) =>
......@@ -432,7 +432,7 @@ public async Task ShutdownRepeated()
using (var startedMre = new ManualResetEvent(initialState: false))
using (var finishedMre = new ManualResetEvent(initialState: false))
using (var serverData = ServerUtil.CreateServer(compilerServerHost: host))
using (var serverData = await ServerUtil.CreateServer(compilerServerHost: host))
{
// Create a compilation that is guaranteed to complete after the shutdown is seen.
host.RunCompilation = (request, cancellationToken) =>
......@@ -468,7 +468,7 @@ public async Task CancelWillCancelCompilation()
{
var host = new TestableCompilerServerHost();
using (var serverData = ServerUtil.CreateServer(compilerServerHost: host))
using (var serverData = await ServerUtil.CreateServer(compilerServerHost: host))
using (var mre = new ManualResetEvent(initialState: false))
{
const int requestCount = 5;
......@@ -520,7 +520,7 @@ public async Task CancelWillCancelCompilation()
[Fact]
public async Task IncorrectProtocolReturnsMismatchedVersionResponse()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var buildResponse = await ServerUtil.Send(serverData.PipeName, new BuildRequest(1, RequestLanguage.CSharpCompile, "abc", new List<BuildRequest.Argument> { }));
Assert.Equal(BuildResponse.ResponseType.MismatchedVersion, buildResponse.Type);
......@@ -530,7 +530,7 @@ public async Task IncorrectProtocolReturnsMismatchedVersionResponse()
[Fact]
public async Task IncorrectServerHashReturnsIncorrectHashResponse()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var buildResponse = await ServerUtil.Send(serverData.PipeName, new BuildRequest(BuildProtocolConstants.ProtocolVersion, RequestLanguage.CSharpCompile, "abc", new List<BuildRequest.Argument> { }));
Assert.Equal(BuildResponse.ResponseType.IncorrectHash, buildResponse.Type);
......@@ -561,14 +561,14 @@ public void QuotePipeName_CoreClr()
}
[Theory]
[InlineData(@"name with space.T.basename", "name with space", true, "basename")]
[InlineData(@"ha_ha.T.basename", @"ha""ha", true, "basename")]
[InlineData(@"jared.T.ha_ha", @"jared", true, @"ha""ha")]
[InlineData(@"jared.F.ha_ha", @"jared", false, @"ha""ha")]
[InlineData(@"jared.F.ha_ha", @"jared", false, @"ha\ha")]
public void GetPipeNameCore(string expectedName, string userName, bool isAdmin, string basePipeName)
[InlineData(@"OLqrNgkgZRf14qL91MdaUn8coiKckUIZCIEkpy0Lt18", "name with space", true, "basename")]
[InlineData(@"8VDiJptv892LtWpeN86z76_YI0Yg0BV6j0SOv8CjQVA", @"ha""ha", true, "basename")]
[InlineData(@"wKSU9psJMbkw+5+TFKLEf94aeslpEb3dDRpAw+9j4nw", @"jared", true, @"ha""ha")]
[InlineData(@"0BDP4_GPWYQh9J_BknwhS9uAZAF_64PK4_VnNsddGZE", @"jared", false, @"ha""ha")]
[InlineData(@"XroHfrjD1FTk7PcXcif2hZdmlVH_L0Pg+RUX01d_uQc", @"jared", false, @"ha\ha")]
public void GetPipeNameCore(string expectedName, string userName, bool isAdmin, string compilerExeDir)
{
Assert.Equal(expectedName, BuildServerConnection.GetPipeNameCore(userName, isAdmin, basePipeName));
Assert.Equal(expectedName, BuildServerConnection.GetPipeName(userName, isAdmin, compilerExeDir));
}
}
}
......@@ -256,16 +256,16 @@ public async Task ServerFailsWithLongTempPathUnix()
new[] { new KeyValuePair<string, string>("TMPDIR", newTempDir.Path) },
async () =>
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(
CSharpCompilerClientExecutable,
$"/shared:{serverData.PipeName} /nologo hello.cs",
_tempDirectory,
s_helloWorldSrcCs,
shouldRunOnServer: false);
shouldRunOnServer: true);
VerifyResultAndOutput(result, _tempDirectory, "Hello, world.");
await serverData.Verify(connections: 0, completed: 0).ConfigureAwait(true);
await serverData.Verify(connections: 1, completed: 1).ConfigureAwait(true);
}
});
}
......@@ -274,7 +274,7 @@ public async Task ServerFailsWithLongTempPathUnix()
public async Task FallbackToCsc()
{
// Verify csc will fall back to command line when server fails to process
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable, $"/shared:{serverData.PipeName} /nologo hello.cs", _tempDirectory, s_helloWorldSrcCs, shouldRunOnServer: false);
VerifyResultAndOutput(result, _tempDirectory, "Hello, world.");
......@@ -287,7 +287,7 @@ public async Task FallbackToCsc()
public async Task CscFallBackOutputNoUtf8()
{
// Verify csc will fall back to command line when server fails to process
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var files = new Dictionary<string, string> { { "hello.cs", "♕" } };
......@@ -303,7 +303,7 @@ public async Task CscFallBackOutputUtf8()
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("♕").Path;
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var result = RunCommandLineCompiler(
CSharpCompilerClientExecutable,
......@@ -324,7 +324,7 @@ public async Task VbcFallbackNoUtf8()
{
var srcFile = _tempDirectory.CreateFile("test.vb").WriteAllText("♕").Path;
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var result = RunCommandLineCompiler(
BasicCompilerClientExecutable,
......@@ -347,7 +347,7 @@ public async Task VbcFallbackUtf8()
{
var srcFile = _tempDirectory.CreateFile("test.vb").WriteAllText("♕").Path;
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var result = RunCommandLineCompiler(
BasicCompilerClientExecutable,
......@@ -368,7 +368,7 @@ public async Task VbcFallbackUtf8()
[Fact]
public async Task FallbackToVbc()
{
using (var serverData = ServerUtil.CreateServerFailsConnection())
using (var serverData = await ServerUtil.CreateServerFailsConnection())
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable, $"/shared:{serverData.PipeName} /nologo /vbruntime* hello.vb", _tempDirectory, s_helloWorldSrcVb, shouldRunOnServer: false);
VerifyResultAndOutput(result, _tempDirectory, "Hello from VB");
......@@ -380,7 +380,7 @@ public async Task FallbackToVbc()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task HelloWorldCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable, $"/shared:{serverData.PipeName} /nologo hello.cs", _tempDirectory, s_helloWorldSrcCs);
VerifyResultAndOutput(result, _tempDirectory, "Hello, world.");
......@@ -392,7 +392,7 @@ public async Task HelloWorldCS()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task HelloWorldCSDashShared()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable, $"-shared:{serverData.PipeName} /nologo hello.cs", _tempDirectory, s_helloWorldSrcCs);
VerifyResultAndOutput(result, _tempDirectory, "Hello, world.");
......@@ -421,7 +421,7 @@ public void CompilerBinariesAreNotX86()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task Platformx86MscorlibCsc()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var files = new Dictionary<string, string> { { "c.cs", "class C {}" } };
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable,
......@@ -437,7 +437,7 @@ public async Task Platformx86MscorlibCsc()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task Platformx86MscorlibVbc()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var files = new Dictionary<string, string> { { "c.vb", "Class C\nEnd Class" } };
var result = RunCommandLineCompiler(BasicCompilerClientExecutable,
......@@ -453,7 +453,7 @@ public async Task Platformx86MscorlibVbc()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task ExtraMSCorLibCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable,
$"/shared:{serverData.PipeName} /nologo /r:mscorlib.dll hello.cs",
......@@ -468,7 +468,7 @@ public async Task ExtraMSCorLibCS()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task HelloWorldVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable,
$"/shared:{serverData.PipeName} /nologo /vbruntime* hello.vb",
......@@ -483,7 +483,7 @@ public async Task HelloWorldVB()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task ExtraMSCorLibVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable,
$"/shared:{serverData.PipeName} /nologo /r:mscorlib.dll /vbruntime* hello.vb",
......@@ -498,7 +498,7 @@ public async Task ExtraMSCorLibVB()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task CompileErrorsCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
Dictionary<string, string> files =
new Dictionary<string, string> {
......@@ -525,7 +525,7 @@ static void Main()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task CompileErrorsVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
Dictionary<string, string> files =
new Dictionary<string, string> {
......@@ -554,7 +554,7 @@ End Sub
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task MissingFileErrorCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable, $"/shared:{serverData.PipeName} missingfile.cs", _tempDirectory);
......@@ -571,7 +571,7 @@ public async Task MissingFileErrorCS()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task MissingReferenceErrorCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable, $"/shared:{serverData.PipeName} /r:missing.dll hello.cs", _tempDirectory, s_helloWorldSrcCs);
......@@ -589,7 +589,7 @@ public async Task MissingReferenceErrorCS()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task InvalidMetadataFileErrorCS()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
Dictionary<string, string> files =
new Dictionary<string, string> {
......@@ -612,7 +612,7 @@ public async Task InvalidMetadataFileErrorCS()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task MissingFileErrorVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable, $"/shared:{serverData.PipeName} /vbruntime* missingfile.vb", _tempDirectory);
......@@ -629,7 +629,7 @@ public async Task MissingFileErrorVB()
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task MissingReferenceErrorVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
Dictionary<string, string> files =
new Dictionary<string, string> {
......@@ -658,7 +658,7 @@ End Sub
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task InvalidMetadataFileErrorVB()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
Dictionary<string, string> files =
new Dictionary<string, string> {
......@@ -701,7 +701,7 @@ End Function
End Class
"}};
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
using (var tmpFile = GetResultFile(rootDirectory, "lib.dll"))
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable, $"src1.vb /shared:{serverData.PipeName} /nologo /t:library /out:lib.dll", rootDirectory, files);
......@@ -803,7 +803,7 @@ public async Task ReferenceCachingCS()
{
TempDirectory rootDirectory = _tempDirectory.CreateDirectory("ReferenceCachingCS");
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
using (var tmpFile = GetResultFile(rootDirectory, "lib.dll"))
{
// Create DLL "lib.dll"
......@@ -969,7 +969,7 @@ End Sub
[Trait(Traits.Environment, Traits.Environments.VSProductInstall)]
public async Task MultipleSimultaneousCompiles()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
// Run this many compiles simultaneously in different directories.
const int numberOfCompiles = 20;
......@@ -1010,7 +1010,7 @@ public static string GetString()
{ return ""library1""; }
}"}};
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable,
$"src1.cs /shared:{serverData.PipeName} /nologo /t:library /out:" + Path.Combine(libDirectory.Path, "lib.dll"),
......@@ -1060,7 +1060,7 @@ End Function
End Class
"}};
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(BasicCompilerClientExecutable,
$"src1.vb /shared:{serverData.PipeName} /vbruntime* /nologo /t:library /out:" + Path.Combine(libDirectory.Path, "lib.dll"),
......@@ -1099,7 +1099,7 @@ public async Task Utf8Output_WithRedirecting_Off_Shared()
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("♕").Path;
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(
CSharpCompilerClientExecutable,
......@@ -1122,7 +1122,7 @@ public async Task Utf8Output_WithRedirecting_Off_Share()
var srcFile = _tempDirectory.CreateFile("test.vb").WriteAllText(@"♕").Path;
var tempOut = _tempDirectory.CreateFile("output.txt");
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(
BasicCompilerClientExecutable,
......@@ -1148,7 +1148,7 @@ public async Task Utf8Output_WithRedirecting_On_Shared_CS()
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("♕").Path;
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(
CSharpCompilerClientExecutable,
......@@ -1170,7 +1170,7 @@ public async Task Utf8Output_WithRedirecting_On_Shared_VB()
{
var srcFile = _tempDirectory.CreateFile("test.vb").WriteAllText(@"♕").Path;
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(
BasicCompilerClientExecutable,
......@@ -1210,7 +1210,7 @@ public System.Exception GetException()
}
"}};
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var result = RunCommandLineCompiler(CSharpCompilerClientExecutable,
$"ref_mscorlib2.cs /shared:{serverData.PipeName} /nologo /nostdlib /noconfig /t:library /r:mscorlib20.dll",
......@@ -1249,7 +1249,7 @@ static void Main(string[] args)
[Fact]
public async Task Utf8OutputInRspFileCsc()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("♕").Path;
var rspFile = _tempDirectory.CreateFile("temp.rsp").WriteAllText(
......@@ -1272,7 +1272,7 @@ public async Task Utf8OutputInRspFileCsc()
[Fact]
public async Task Utf8OutputInRspFileVbc()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("♕").Path;
var rspFile = _tempDirectory.CreateFile("temp.rsp").WriteAllText(
......@@ -1337,7 +1337,7 @@ public void BadKeepAlive4()
[WorkItem(1024619, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1024619")]
public async Task Bug1024619_01()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var srcFile = _tempDirectory.CreateFile("test.cs").WriteAllText("").Path;
......@@ -1370,7 +1370,7 @@ public async Task Bug1024619_01()
[WorkItem(1024619, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1024619")]
public async Task Bug1024619_02()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
var srcFile = _tempDirectory.CreateFile("test.vb").WriteAllText("").Path;
......@@ -1426,7 +1426,7 @@ public async Task MissingCompilerAssembly_CompilerServerHost()
{
throw new FileNotFoundException();
});
using (var serverData = ServerUtil.CreateServer(compilerServerHost: host))
using (var serverData = await ServerUtil.CreateServer(compilerServerHost: host))
{
var request = new BuildRequest(1, RequestLanguage.CSharpCompile, string.Empty, new BuildRequest.Argument[0]);
var compileTask = ServerUtil.Send(serverData.PipeName, request);
......
......@@ -115,7 +115,7 @@ private bool TryCreateServer(string pipeName)
return false;
}
var serverData = ServerUtil.CreateServer(pipeName);
var serverData = ServerUtil.CreateServer(pipeName).GetAwaiter().GetResult();
_serverDataList.Add(serverData);
return true;
}
......@@ -406,22 +406,22 @@ public void KeepAlive(char optionPrefix)
public class MiscTest
{
[Fact]
public void GetBasePipeNameSlashes()
public void GetPipeNameForPathOptSlashes()
{
var path = string.Format(@"q:{0}the{0}path", Path.DirectorySeparatorChar);
var name = BuildServerConnection.GetBasePipeName(path);
Assert.Equal(name, BuildServerConnection.GetBasePipeName(path));
Assert.Equal(name, BuildServerConnection.GetBasePipeName(path + Path.DirectorySeparatorChar));
Assert.Equal(name, BuildServerConnection.GetBasePipeName(path + Path.DirectorySeparatorChar + Path.DirectorySeparatorChar));
var name = BuildServerConnection.GetPipeNameForPathOpt(path);
Assert.Equal(name, BuildServerConnection.GetPipeNameForPathOpt(path));
Assert.Equal(name, BuildServerConnection.GetPipeNameForPathOpt(path + Path.DirectorySeparatorChar));
Assert.Equal(name, BuildServerConnection.GetPipeNameForPathOpt(path + Path.DirectorySeparatorChar + Path.DirectorySeparatorChar));
}
[Fact]
public void GetBasePipeNameLength()
public void GetPipeNameForPathOptLength()
{
var path = string.Format(@"q:{0}the{0}path", Path.DirectorySeparatorChar);
var name = BuildServerConnection.GetBasePipeName(path);
var name = BuildServerConnection.GetPipeNameForPathOpt(path);
// We only have ~50 total bytes to work with on mac, so the base path must be small
Assert.InRange(name.Length, 10, 30);
Assert.Equal(43, name.Length);
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests
......@@ -45,32 +38,5 @@ public void NoSetting()
Assert.Equal(ServerDispatcher.DefaultServerKeepAlive, _controller.GetKeepAliveTimeout());
}
}
[ConditionalFact(typeof(UnixLikeOnly))]
public void RunServerWithLongTempPath()
{
var pipeName = Guid.NewGuid().ToString("N");
// Make a really long path. This should work on Windows, which doesn't rely on temp path,
// but not on Unix, which has a max path length
var tempPath = new string('a', 100);
// This test fails by spinning forever. If the path is not seen as invalid, the server
// starts up and will never return.
Assert.Equal(CommonCompiler.Failed, DesktopBuildServerController.RunServer(pipeName, tempPath: tempPath));
}
[ConditionalFact(typeof(UnixLikeOnly))]
public void RunServerWithLongTempPathInstance()
{
var pipeName = Guid.NewGuid().ToString("N");
// Make a really long path. This should work on Windows, which doesn't rely on temp path,
// but not on Unix, which has a max path length
var tempPath = new string('a', 100);
BuildServerController buildServerController = new DesktopBuildServerController(new NameValueCollection());
// This test fails by spinning forever. If the path is not seen as invalid, the server
// starts up and will never return.
Assert.Equal(CommonCompiler.Failed, DesktopBuildServerController.RunServer(pipeName, tempPath: tempPath));
}
}
}
......@@ -31,7 +31,7 @@ private async Task<(byte[] assemblyBytes, string finalFlags)> CompileAndGetBytes
try
{
string finalFlags = null;
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
finalFlags = $"{ _flags } /shared:{ serverData.PipeName } /pathmap:{tempDir.Path}=/ /out:{ outFile } { srcFile }";
var result = CompilerServerUnitTests.RunCommandLineCompiler(
......
......@@ -80,7 +80,7 @@ internal static BuildPaths CreateBuildPaths(string workingDir, string tempDir)
tempDir: tempDir);
}
internal static ServerData CreateServer(
internal static async Task<ServerData> CreateServer(
string pipeName = null,
ICompilerServerHost compilerServerHost = null,
bool failingServer = false,
......@@ -101,7 +101,7 @@ internal static BuildPaths CreateBuildPaths(string workingDir, string tempDir)
var serverListenSource = new TaskCompletionSource<bool>();
var cts = new CancellationTokenSource();
var mutexName = BuildServerConnection.GetServerMutexName(pipeName);
var thread = new Thread(_ =>
var task = Task.Run(() =>
{
var listener = new TestableDiagnosticListener();
listener.Listening += (sender, e) => { serverListenSource.TrySetResult(true); };
......@@ -122,13 +122,16 @@ internal static BuildPaths CreateBuildPaths(string workingDir, string tempDir)
}
});
thread.Start();
// The contract of this function is that it will return once the server has started. Spin here until
// we can verify the server has started or simply failed to start.
while (BuildServerConnection.WasServerMutexOpen(mutexName) != true && thread.IsAlive)
while (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !task.IsCompleted)
{
await Task.Yield();
}
if (task.IsFaulted)
{
Thread.Yield();
throw task.Exception;
}
return new ServerData(cts, pipeName, serverStatsSource.Task, serverListenSource.Task);
......@@ -137,7 +140,7 @@ internal static BuildPaths CreateBuildPaths(string workingDir, string tempDir)
/// <summary>
/// Create a compiler server that fails all connections.
/// </summary>
internal static ServerData CreateServerFailsConnection(string pipeName = null)
internal static Task<ServerData> CreateServerFailsConnection(string pipeName = null)
{
return CreateServer(pipeName, failingServer: true);
}
......
......@@ -6,6 +6,7 @@
using System.Collections.Specialized;
using System.IO;
using System.IO.Pipes;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
......@@ -39,7 +40,7 @@ private static Task<int> RunShutdownAsync(string pipeName, bool waitForProcess =
[Fact]
public async Task Standard()
{
using (var serverData = ServerUtil.CreateServer())
using (var serverData = await ServerUtil.CreateServer())
{
// Make sure the server is listening for this particular test.
await serverData.ListenTask;
......@@ -62,7 +63,7 @@ public async Task NoServerMutex()
Assert.Equal(CommonCompiler.Succeeded, exitCode);
}
[ConditionalFact(typeof(WindowsOrLinuxOnly))]
[Fact]
[WorkItem(34880, "https://github.com/dotnet/roslyn/issues/34880")]
public async Task NoServerConnection()
{
......@@ -77,7 +78,7 @@ public async Task NoServerConnection()
var thread = new Thread(() =>
{
using (var mutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out created))
using (var stream = new NamedPipeServerStream(pipeName))
using (var stream = NamedPipeUtil.CreateServer(pipeName))
{
readyMre.Set();
......@@ -112,7 +113,7 @@ public async Task NoServerConnection()
/// the client can error out.
/// </summary>
/// <returns></returns>
[ConditionalFact(typeof(WindowsOrLinuxOnly))]
[Fact]
[WorkItem(34880, "https://github.com/dotnet/roslyn/issues/34880")]
public async Task ServerShutdownsDuringProcessing()
{
......@@ -126,7 +127,7 @@ public async Task ServerShutdownsDuringProcessing()
var thread = new Thread(() =>
{
using (var stream = new NamedPipeServerStream(pipeName))
using (var stream = NamedPipeUtil.CreateServer(pipeName))
{
var mutex = new Mutex(initiallyOwned: true, name: mutexName, createdNew: out created);
readyMre.Set();
......@@ -159,6 +160,21 @@ public async Task ServerShutdownsDuringProcessing()
Assert.True(created);
}
}
[Fact]
public async Task RunServerWithLongTempPath()
{
string pipeName = BuildServerConnection.GetPipeNameForPathOpt(Guid.NewGuid().ToString());
string tempPath = new string('a', 100);
using (var serverData = await ServerUtil.CreateServer(pipeName, tempPath: tempPath))
{
// Make sure the server is listening for this particular test.
await serverData.ListenTask;
var exitCode = await RunShutdownAsync(serverData.PipeName, waitForProcess: false).ConfigureAwait(false);
Assert.Equal(CommonCompiler.Succeeded, exitCode);
await serverData.Verify(connections: 1, completed: 1);
}
}
}
public class ParseCommandLineTests : VBCSCompilerServerTests
......
......@@ -65,11 +65,7 @@ internal sealed class BuildServerConnection
/// <summary>
/// Determines if the compiler server is supported in this environment.
/// </summary>
internal static bool IsCompilerServerSupported(string tempPath)
{
var pipeName = GetPipeNameForPathOpt("");
return pipeName != null && !IsPipePathTooLong(pipeName, tempPath);
}
internal static bool IsCompilerServerSupported(string tempPath) => GetPipeNameForPathOpt("") is object;
public static Task<BuildResponse> RunServerCompilation(
RequestLanguage language,
......@@ -310,14 +306,6 @@ internal static bool IsCompilerServerSupported(string tempPath)
NamedPipeClientStream pipeStream;
try
{
// If the pipe path would be too long, there cannot be a server at the other end.
// We're not using a saved temp path here because pipes are created with
// Path.GetTempPath() in corefx NamedPipeClientStream and we want to replicate that behavior.
if (IsPipePathTooLong(pipeName, Path.GetTempPath()))
{
return null;
}
// Machine-local named pipes are named "\\.\pipe\<pipename>".
// We use the SHA1 of the directory the compiler exes live in as the pipe name.
// The NamedPipeClientStream class handles the "\\.\pipe\" part for us.
......@@ -462,8 +450,6 @@ internal static bool TryCreateServerCore(string clientDir, string pipeName)
/// </returns>
internal static string GetPipeNameForPathOpt(string compilerExeDirectory)
{
var basePipeName = GetBasePipeName(compilerExeDirectory);
// Prefix with username and elevation
bool isAdmin = false;
if (PlatformInformation.IsWindows)
......@@ -479,65 +465,27 @@ internal static string GetPipeNameForPathOpt(string compilerExeDirectory)
return null;
}
return GetPipeNameCore(userName, isAdmin, basePipeName);
}
internal static string GetPipeNameCore(string userName, bool isAdmin, string basePipeName)
{
var pipeName = $"{userName}.{(isAdmin ? 'T' : 'F')}.{basePipeName}";
// The pipe name is passed between processes as a command line argument as a
// quoted value. Unfortunately we can't use ProcessStartInfo.ArgumentList as
// we still target net472 (API only available on CoreClr + netstandard). To
// make the problem approachable we remove the troublesome characters.
//
// This does mean if two users on the same machine are building simultaneously
// and the user names differ only be a " or / and a _ then there will be a
// conflict. That seems rather obscure though.
return pipeName
.Replace('"', '_')
.Replace('\\', '_');
}
/// <summary>
/// Check if our constructed path is too long. On some Unix machines the pipe is a
/// real file in the temp directory, and there is a limit on how long the path can
/// be. This will never be true on Windows.
/// </summary>
internal static bool IsPipePathTooLong(string pipeName, string tempPath)
{
if (PlatformInformation.IsUnix)
{
// This is the maximum path length of Unix Domain Sockets on a number of systems.
// Since CoreFX implements named pipes using Unix Domain Sockets, if we exceed this
// length than the pipe will fail.
// This number is considered the smallest known max length according to
// http://man7.org/linux/man-pages/man7/unix.7.html
const int MaxPipePathLength = 92;
const int PrefixLength = 11; // "CoreFxPipe_".Length
return (tempPath.Length + PrefixLength + pipeName.Length) > MaxPipePathLength;
}
return false;
return GetPipeName(userName, isAdmin, compilerExeDirectory);
}
internal static string GetBasePipeName(string compilerExeDirectory)
internal static string GetPipeName(
string userName,
bool isAdmin,
string compilerExeDirectory)
{
// Normalize away trailing slashes. File APIs include / exclude this with no
// discernable pattern. Easiest to normalize it here vs. auditing every caller
// of this method.
compilerExeDirectory = compilerExeDirectory.TrimEnd(Path.DirectorySeparatorChar);
string basePipeName;
var pipeNameInput = $"{userName}.{isAdmin}.{compilerExeDirectory}";
using (var sha = SHA256.Create())
{
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(compilerExeDirectory));
basePipeName = Convert.ToBase64String(bytes)
.Substring(0, 10) // We only have ~50 total characters on Mac, so strip this down
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(pipeNameInput));
return Convert.ToBase64String(bytes)
.Replace("/", "_")
.Replace("=", string.Empty);
}
return basePipeName;
}
internal static bool WasServerMutexOpen(string mutexName)
......
......@@ -16,15 +16,36 @@ namespace Microsoft.CodeAnalysis
/// </summary>
internal static class NamedPipeUtil
{
// Size of the buffers to use: 64K
private const int PipeBufferSize = 0x10000;
private static string GetPipeNameOrPath(string pipeName)
{
if (PlatformInformation.IsUnix)
{
// If we're on a Unix machine then named pipes are implemented using Unix Domain Sockets.
// Most Unix systems have a maximum path length limit for Unix Domain Sockets, with
// Mac having a particularly short one. Mac also has a generated temp directory that
// can be quite long, leaving very little room for the actual pipe name. Fortunately,
// '/tmp' is mandated by POSIX to always be a valid temp directory, so we can use that
// instead.
return Path.Combine("/tmp", pipeName);
}
else
{
return pipeName;
}
}
/// <summary>
/// Create a client for the current user only.
/// </summary>
internal static NamedPipeClientStream CreateClient(string serverName, string pipeName, PipeDirection direction, PipeOptions options) =>
new NamedPipeClientStream(serverName, pipeName, direction, options | CurrentUserOption);
internal static NamedPipeClientStream CreateClient(string serverName, string pipeName, PipeDirection direction, PipeOptions options)
=> new NamedPipeClientStream(serverName, GetPipeNameOrPath(pipeName), direction, options | CurrentUserOption);
/// <summary>
/// Does the client of "pipeStream" have the same identity and elevation as we do? The <see cref="CreateClient"/> and
/// <see cref="CreateServer" /> methods will already guarantee that the identity of the client and server are the
/// <see cref="CreateServer(string)" /> methods will already guarantee that the identity of the client and server are the
/// same. This method is attempting to validate that the elevation level is the same between both ends of the
/// named pipe (want to disallow low priv session sending compilation requests to an elevated one).
/// </summary>
......@@ -53,6 +74,22 @@ internal static bool CheckClientElevationMatches(NamedPipeServerStream pipeStrea
return true;
}
/// <summary>
/// Create a server for the current user only
/// </summary>
internal static NamedPipeServerStream CreateServer(string pipeName)
{
var pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
return CreateServer(
pipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
pipeOptions,
PipeBufferSize,
PipeBufferSize);
}
#if NET472
const int s_currentUserOnlyValue = unchecked((int)0x20000000);
......@@ -65,7 +102,7 @@ internal static bool CheckClientElevationMatches(NamedPipeServerStream pipeStrea
? (PipeOptions)s_currentUserOnlyValue
: PipeOptions.None;
internal static NamedPipeServerStream CreateServer(
private static NamedPipeServerStream CreateServer(
string pipeName,
PipeDirection direction,
int maxNumberOfServerInstances,
......@@ -74,7 +111,7 @@ internal static bool CheckClientElevationMatches(NamedPipeServerStream pipeStrea
int inBufferSize,
int outBufferSize) =>
new NamedPipeServerStream(
pipeName,
GetPipeNameOrPath(pipeName),
direction,
maxNumberOfServerInstances,
transmissionMode,
......@@ -135,7 +172,7 @@ internal static PipeSecurity CreatePipeSecurity()
// Validation is handled by CurrentUserOnly
internal static PipeSecurity CreatePipeSecurity() => null;
internal static NamedPipeServerStream CreateServer(
private static NamedPipeServerStream CreateServer(
string pipeName,
PipeDirection direction,
int maxNumberOfServerInstances,
......@@ -144,7 +181,7 @@ internal static PipeSecurity CreatePipeSecurity()
int inBufferSize,
int outBufferSize) =>
new NamedPipeServerStream(
pipeName,
GetPipeNameOrPath(pipeName),
direction,
maxNumberOfServerInstances,
transmissionMode,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册