提交 78cdde3b 编写于 作者: J Jared Parsons 提交者: Jared Parsons

Server supports shutdown request

上级 9fece4e2
......@@ -151,7 +151,7 @@ public static async Task<BuildRequest> ReadAsync(Stream inStream, CancellationTo
/// <summary>
/// Write a Request to the stream.
/// </summary>
public async Task WriteAsync(Stream outStream, CancellationToken cancellationToken)
public async Task WriteAsync(Stream outStream, CancellationToken cancellationToken = default(CancellationToken))
{
using (var memoryStream = new MemoryStream())
using (var writer = new BinaryWriter(memoryStream, Encoding.Unicode))
......@@ -305,7 +305,7 @@ public enum ResponseType
/// <param name="stream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<BuildResponse> ReadAsync(Stream stream, CancellationToken cancellationToken)
public static async Task<BuildResponse> ReadAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
{
Log("Reading response length");
// Read the response length
......
......@@ -2,6 +2,7 @@
using Roslyn.Utilities;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
......@@ -45,6 +46,11 @@ internal enum CompletionReason
/// There was an unhandled exception processing the result.
/// </summary>
ClientException,
/// <summary>
/// There was a request from the client to shutdown the server.
/// </summary>
ClientShutdownRequest,
}
/// <summary>
......@@ -100,42 +106,14 @@ public async Task<ConnectionData> HandleConnection(CancellationToken cancellatio
return new ConnectionData(CompletionReason.CompilationNotStarted);
}
var keepAlive = CheckForNewKeepAlive(request);
// Kick off both the compilation and a task to monitor the pipe for closing.
var buildCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var compilationTask = ServeBuildRequest(request, buildCts.Token);
var monitorTask = CreateMonitorDisconnectTask(buildCts.Token);
await Task.WhenAny(compilationTask, monitorTask).ConfigureAwait(false);
// Do an 'await' on the completed task, preference being compilation, to force
// any exceptions to be realized in this method for logging.
CompletionReason reason;
if (compilationTask.IsCompleted)
if (IsShutdownRequest(request))
{
var response = await compilationTask.ConfigureAwait(false);
try
{
Log("Begin writing response.");
await response.WriteAsync(_stream, cancellationToken).ConfigureAwait(false);
reason = CompletionReason.CompilationCompleted;
Log("End writing response.");
}
catch
{
reason = CompletionReason.ClientDisconnect;
}
return await HandleShutdownRequest(cancellationToken).ConfigureAwait(false);
}
else
{
await monitorTask.ConfigureAwait(false);
reason = CompletionReason.ClientDisconnect;
return await HandleCompilationRequest(request, cancellationToken).ConfigureAwait(false);
}
// Begin the tear down of the Task which didn't complete.
buildCts.Cancel();
return new ConnectionData(reason, keepAlive);
}
finally
{
......@@ -143,6 +121,54 @@ public async Task<ConnectionData> HandleConnection(CancellationToken cancellatio
}
}
private async Task<ConnectionData> HandleCompilationRequest(BuildRequest request, CancellationToken cancellationToken)
{
var keepAlive = CheckForNewKeepAlive(request);
// Kick off both the compilation and a task to monitor the pipe for closing.
var buildCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var compilationTask = ServeBuildRequest(request, buildCts.Token);
var monitorTask = CreateMonitorDisconnectTask(buildCts.Token);
await Task.WhenAny(compilationTask, monitorTask).ConfigureAwait(false);
// Do an 'await' on the completed task, preference being compilation, to force
// any exceptions to be realized in this method for logging.
CompletionReason reason;
if (compilationTask.IsCompleted)
{
var response = await compilationTask.ConfigureAwait(false);
try
{
Log("Begin writing response.");
await response.WriteAsync(_stream, cancellationToken).ConfigureAwait(false);
reason = CompletionReason.CompilationCompleted;
Log("End writing response.");
}
catch
{
reason = CompletionReason.ClientDisconnect;
}
}
else
{
await monitorTask.ConfigureAwait(false);
reason = CompletionReason.ClientDisconnect;
}
// Begin the tear down of the Task which didn't complete.
buildCts.Cancel();
return new ConnectionData(reason, keepAlive);
}
private async Task<ConnectionData> HandleShutdownRequest(CancellationToken cancellationToken)
{
var id = Process.GetCurrentProcess().Id;
var response = new ShutdownBuildResponse(id);
await response.WriteAsync(_stream, cancellationToken).ConfigureAwait(false);
return new ConnectionData(CompletionReason.ClientShutdownRequest);
}
/// <summary>
/// Check the request arguments for a new keep alive time. If one is present,
/// set the server timer to the new time.
......@@ -169,6 +195,11 @@ public async Task<ConnectionData> HandleConnection(CancellationToken cancellatio
return timeout;
}
private bool IsShutdownRequest(BuildRequest request)
{
return request.Arguments.Length == 1 && request.Arguments[0].ArgumentId == BuildProtocolConstants.ArgumentId.Shutdown;
}
protected virtual Task<BuildResponse> ServeBuildRequest(BuildRequest request, CancellationToken cancellationToken)
{
return Task.Run(() =>
......
......@@ -169,10 +169,10 @@ private void WaitForAnyCompletion(IEnumerable<Task<ConnectionData>> e, Task[] ot
/// <summary>
/// Checks the completed connection objects.
/// </summary>
/// <returns>True if everything completed normally and false if there were any client disconnections.</returns>
/// <returns>False if the server needs to begin shutting down</returns>
private bool CheckConnectionTask(List<Task<ConnectionData>> connectionList, ref TimeSpan? keepAlive, ref bool isKeepAliveDefault)
{
var allFine = true;
var shutdown = false;
var processedCount = 0;
var i = 0;
while (i < connectionList.Count)
......@@ -189,9 +189,24 @@ private bool CheckConnectionTask(List<Task<ConnectionData>> connectionList, ref
var connectionData = current.Result;
ChangeKeepAlive(connectionData.KeepAlive, ref keepAlive, ref isKeepAliveDefault);
if (connectionData.CompletionReason == CompletionReason.ClientDisconnect || connectionData.CompletionReason == CompletionReason.ClientException)
switch (connectionData.CompletionReason)
{
allFine = false;
case CompletionReason.CompilationCompleted:
case CompletionReason.CompilationNotStarted:
// These are all normal shutdown states. Nothing to do here.
break;
case CompletionReason.ClientDisconnect:
// Have to assume the worst here which is user pressing Ctrl+C at the command line and
// hence wanting all compilation to end.
shutdown = true;
break;
case CompletionReason.ClientException:
case CompletionReason.ClientShutdownRequest:
shutdown = true;
break;
default:
throw new InvalidOperationException($"Unexpected enum value {connectionData.CompletionReason}");
}
}
......@@ -200,7 +215,7 @@ private bool CheckConnectionTask(List<Task<ConnectionData>> connectionList, ref
_diagnosticListener.ConnectionCompleted(processedCount);
}
return allFine;
return !shutdown;
}
private void ChangeKeepAlive(TimeSpan? value, ref TimeSpan? keepAlive, ref bool isKeepAliveDefault)
......
......@@ -249,7 +249,6 @@ public async Task KeepAliveAfterSimultaneousConnection()
dispatcher.ListenAndDispatchConnections(keepAlive);
});
await readySource.Task.ConfigureAwait(true);
foreach (var source in list)
{
......
......@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
......@@ -1359,7 +1360,7 @@ public async Task ServerRespectsAppConfig()
var exited = proc.HasExited;
if (!exited)
{
proc.Kill();
Kill(proc);
Assert.True(false, "Compiler server did not exit in time");
}
}
......@@ -1408,7 +1409,28 @@ public void BadKeepAlive4()
Assert.Equal("", result.Errors);
}
[Fact, WorkItem(1024619, "DevDiv")]
[Fact]
public async Task ShutdownRequestDirect()
{
using (var serverData = ServerUtil.CreateServer())
using (var client = new NamedPipeClientStream(serverData.PipeName))
{
await client.ConnectAsync();
var memoryStream = new MemoryStream();
await BuildRequest.CreateShutdown().WriteAsync(memoryStream);
memoryStream.Position = 0;
await memoryStream.CopyToAsync(client);
var response = await BuildResponse.ReadAsync(client);
Assert.Equal(BuildResponse.ResponseType.Shutdown, response.Type);
Assert.Equal(Process.GetCurrentProcess().Id, ((ShutdownBuildResponse)response).ServerProcessId);
await Verify(serverData, connections: 1, completed: 1);
}
}
[Fact]
[WorkItem(1024619, "DevDiv")]
public async Task Bug1024619_01()
{
using (var serverData = ServerUtil.CreateServer())
......@@ -1440,7 +1462,8 @@ public async Task Bug1024619_01()
}
}
[Fact, WorkItem(1024619, "DevDiv")]
[Fact]
[WorkItem(1024619, "DevDiv")]
public async Task Bug1024619_02()
{
using (var serverData = ServerUtil.CreateServer())
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册