Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dotnet watch causing server to always fail #8006

Merged
merged 8 commits into from
Sep 29, 2022
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
15 changes: 12 additions & 3 deletions src/Build/BackEnd/Node/OutOfProcServerNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public delegate (int exitCode, string exitType) BuildCallback(
/// </summary>
private Exception? _shutdownException = null;

/// <summary>
/// Indicate that cancel has been requested and initiated.
/// </summary>
private bool _cancelRequested = false;
private string _serverBusyMutexName = default!;

public OutOfProcServerNode(BuildCallback buildFunction)
Expand Down Expand Up @@ -312,7 +316,12 @@ private void HandleServerShutdownCommand(NodeBuildComplete buildComplete)
_shutdownEvent.Set();
}

private static void HandleBuildCancel() => BuildManager.DefaultBuildManager.CancelAllSubmissions();
private void HandleBuildCancel()
{
CommunicationsUtilities.Trace("Received request to cancel build running on MSBuild Server. MSBuild server will shutdown.}");
_cancelRequested = true;
BuildManager.DefaultBuildManager.CancelAllSubmissions();
}

private void HandleServerNodeBuildCommandAsync(ServerNodeBuildCommand command)
{
Expand Down Expand Up @@ -411,10 +420,10 @@ private void HandleServerNodeBuildCommand(ServerNodeBuildCommand command)
var response = new ServerNodeBuildResult(buildResult.exitCode, buildResult.exitType);
SendPacket(response);

_shutdownReason = NodeEngineShutdownReason.BuildCompleteReuse;
// Shutdown server if cancel was requested. This is consistent with nodes behavior.
_shutdownReason = _cancelRequested ? NodeEngineShutdownReason.BuildComplete : NodeEngineShutdownReason.BuildCompleteReuse;
_shutdownEvent.Set();
}

internal sealed class RedirectConsoleWriter : StringWriter
{
private readonly Action<string> _writeCallback;
Expand Down
54 changes: 32 additions & 22 deletions src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -993,9 +993,8 @@ private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs
return;
}

s_buildCancellationSource.Cancel();

Console.WriteLine(ResourceUtilities.GetResourceString("AbortingBuild"));
s_buildCancellationSource.Cancel();

// The OS takes a lock in
// kernel32.dll!_SetConsoleCtrlHandler, so if a task
Expand All @@ -1006,31 +1005,42 @@ private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs
// We're already on a threadpool thread anyway.
WaitCallback callback = delegate
{
s_cancelComplete.Reset();

// If the build is already complete, just exit.
if (s_buildComplete.WaitOne(0))
try
{
s_cancelComplete.Set();
return;
}
s_cancelComplete.Reset();

// If the build has already started (or already finished), we will cancel it
// If the build has not yet started, it will cancel itself, because
// we set alreadyCalled=1
bool hasBuildStarted;
lock (s_buildLock)
{
hasBuildStarted = s_hasBuildStarted;
}
// If the build is already complete, just exit.
if (s_buildComplete.WaitOne(0))
{
s_cancelComplete.Set();
return;
}

if (hasBuildStarted)
// If the build has already started (or already finished), we will cancel it
// If the build has not yet started, it will cancel itself, because
// we set alreadyCalled=1
bool hasBuildStarted;
lock (s_buildLock)
{
hasBuildStarted = s_hasBuildStarted;
}

if (hasBuildStarted)
{
BuildManager.DefaultBuildManager.CancelAllSubmissions();
s_buildComplete.WaitOne();
}

s_cancelComplete.Set(); // This will release our main Execute method so we can finally exit.
}
finally
{
BuildManager.DefaultBuildManager.CancelAllSubmissions();
s_buildComplete.WaitOne();
// Server node shall terminate after it received CancelKey press.
if (s_isServerNode)
{
Environment.Exit(0); // the process can now be terminated as everything has already been gracefully cancelled.
Copy link
Member

Choose a reason for hiding this comment

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

I'm confused—why does the server have to shut down when it's cancelled? I would've expected that just cancelling submissions then waiting for the next build request would've been sufficient.

Copy link
Contributor Author

@rokonec rokonec Sep 28, 2022

Choose a reason for hiding this comment

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

To be consistent with other nodes behavior. When you cancel builds all build nodes are shutdown afterward.
I am not sure why, but the code shows clear intent to do so. I guess, we do not want to deal with processes which might ended up be in inconsistent states caused by build cancellation.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. It seems reasonable to want to avoid unknown states at build start. I don't know what would go wrong in this case, but leaving yourself open to unforeseen bugs is bad if it's avoidable, so it seems reasonable to align with that.

}
}

s_cancelComplete.Set(); // This will release our main Execute method so we can finally exit.
};

ThreadPoolExtensions.QueueThreadPoolWorkItemWithCulture(callback, CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture);
Expand Down