From db4efaecbcc4346b9033a188f770c2b3fb08d98a Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Fri, 17 Apr 2026 17:47:22 +0700 Subject: [PATCH 1/3] Retry pipe connection in TryShutdownServer to fix flaky CanShutdownServerProcess test --- src/Build/BackEnd/Client/MSBuildClient.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index 34f1712c12b..97d6fd173df 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -255,8 +255,24 @@ private bool TryShutdownServer(CancellationToken cancellationToken) return false; } - // Connect to server. - if (!TryConnectToServer(1_000)) + // Connect to server with retry. The server may be recycling its pipe after a previous build: + // the ServerBusy mutex is released before the new pipe is ready, causing TryConnectToServer + // to silently return false (HandshakeStatus.Timeout). Retry for up to 5 seconds to ride out + // the transitional window. + bool connected = false; + Stopwatch connectSw = Stopwatch.StartNew(); + while (!connected && connectSw.ElapsedMilliseconds < 5_000) + { + connected = TryConnectToServer(1_000); + if (!connected && connectSw.ElapsedMilliseconds < 5_000) + { + CommunicationsUtilities.Trace("Failed to connect to idle server, will retry..."); + Thread.Sleep(100); + CreateNodePipeStream(); + } + } + + if (!connected) { CommunicationsUtilities.Trace("Client cannot connect to idle server to shut it down."); return false; From bfe530f96498f273240cae71d8a9f600268ab9fb Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Fri, 17 Apr 2026 18:43:23 +0700 Subject: [PATCH 2/3] Stabilize CanShutdownServerProcess test: retry pipe connection on shutdown --- src/Build/BackEnd/Client/MSBuildClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index 97d6fd173df..c48932249ce 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -128,6 +128,8 @@ public MSBuildClient(string[] commandLine, string msbuildLocation) private void CreateNodePipeStream() { + _nodeStream?.Dispose(); + _packetPump?.Dispose(); #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter _nodeStream = new NamedPipeClientStream( serverName: ".", From ec459283352f2edb9a421e7594d86fe282be598c Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Wed, 6 May 2026 13:38:52 +0700 Subject: [PATCH 3/3] Stabilize CanShutdownServerProcess(byBuildManager: True) test --- src/Build/BackEnd/Client/MSBuildClient.cs | 22 ++------------------ src/MSBuild.UnitTests/MSBuildServer_Tests.cs | 14 ++++++++++--- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index 2200679cbf6..c1073683256 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -128,8 +128,6 @@ public MSBuildClient(string[] commandLine, string msbuildLocation) private void CreateNodePipeStream() { - _nodeStream?.Dispose(); - _packetPump?.Dispose(); #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter _nodeStream = new NamedPipeClientStream( serverName: ".", @@ -257,24 +255,8 @@ private bool TryShutdownServer(CancellationToken cancellationToken) return false; } - // Connect to server with retry. The server may be recycling its pipe after a previous build: - // the ServerBusy mutex is released before the new pipe is ready, causing TryConnectToServer - // to silently return false (HandshakeStatus.Timeout). Retry for up to 5 seconds to ride out - // the transitional window. - bool connected = false; - Stopwatch connectSw = Stopwatch.StartNew(); - while (!connected && connectSw.ElapsedMilliseconds < 5_000) - { - connected = TryConnectToServer(1_000); - if (!connected && connectSw.ElapsedMilliseconds < 5_000) - { - CommunicationsUtilities.Trace("Failed to connect to idle server, will retry..."); - Thread.Sleep(100); - CreateNodePipeStream(); - } - } - - if (!connected) + // Connect to server. + if (!TryConnectToServer(1_000)) { CommunicationsUtilities.Trace("Client cannot connect to idle server to shut it down."); return false; diff --git a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs index 1531c665f54..1f196298a2d 100644 --- a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs +++ b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs @@ -253,16 +253,24 @@ public void CanShutdownServerProcess(bool byBuildManager) if (byBuildManager) { - BuildManager.DefaultBuildManager.ShutdownAllNodes(); + // ShutdownAllNodes does not expose whether the MSBuildServer was successfully + // contacted. The server may be recycling its pipe right after the build completes + // (busy mutex released before new pipe is ready), causing the first attempt to + // silently fail. Retry until the process exits or a reasonable timeout elapses. + Stopwatch sw = Stopwatch.StartNew(); + while (!serverProcess.HasExited && sw.ElapsedMilliseconds < 10_000) + { + BuildManager.DefaultBuildManager.ShutdownAllNodes(); + serverProcess.WaitForExit(500); + } } else { bool serverIsDown = MSBuildClient.ShutdownServer(CancellationToken.None); serverIsDown.ShouldBeTrue(); + serverProcess.WaitForExit(10_000); } - serverProcess.WaitForExit(10_000); - serverProcess.HasExited.ShouldBeTrue(); }