From 811627c261b3c0eeccf175379da6d7a504c36042 Mon Sep 17 00:00:00 2001 From: JakeRadMSFT Date: Mon, 9 Mar 2026 23:36:51 -0700 Subject: [PATCH] Fix Unix SessionId in handshake to enable cross-terminal node reuse On Unix, Process.SessionId returns the session leader PID (from getsid()), which differs per terminal. This prevents nodes launched from one terminal from being reused by builds in another terminal. On Windows, SessionId correctly differentiates RDP sessions. Fix: Use SessionId=0 on non-Windows platforms, since Unix doesn't need RDP-style session isolation. This restores effective node reuse across terminals on macOS and Linux. Tests use [UnixOnlyFact] and directly assert SessionId=0 in the key. Note: The MSBuildTaskHost copy (src/MSBuildTaskHost/) has the same pattern but only runs on .NET Framework/Windows, so it's not affected. --- .../BackEnd/UnixNodeReuseFixes_Tests.cs | 44 +++++++++++++++++++ src/Shared/CommunicationsUtilities.cs | 6 ++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/Build.UnitTests/BackEnd/UnixNodeReuseFixes_Tests.cs diff --git a/src/Build.UnitTests/BackEnd/UnixNodeReuseFixes_Tests.cs b/src/Build.UnitTests/BackEnd/UnixNodeReuseFixes_Tests.cs new file mode 100644 index 00000000000..5e098a01fc4 --- /dev/null +++ b/src/Build.UnitTests/BackEnd/UnixNodeReuseFixes_Tests.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET + +using Microsoft.Build.Internal; +using Microsoft.Build.Shared; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.UnitTests +{ + /// + /// Tests for Unix node reuse bug fixes: + /// - SessionId = 0 on Unix (cross-terminal node reuse) + /// + public class UnixNodeReuseFixes_Tests + { + [UnixOnlyFact] + public void Handshake_OnUnix_SessionIdIsZero() + { + var handshake = new Handshake(HandshakeOptions.NodeReuse); + + // Use the structured accessor rather than parsing the key string + handshake.RetrieveHandshakeComponents().SessionId.ShouldBe(0, + "Unix handshake SessionId should be 0 to enable cross-terminal node reuse"); + } + + [UnixOnlyFact] + public void Handshake_OnUnix_KeyIsDeterministic() + { + // Two handshakes with same options produce identical keys on Unix + // because SessionId is always 0 (not terminal-specific). + // Note: this runs in a single process, so it validates determinism + // rather than cross-terminal behavior (which requires integration testing). + var h1 = new Handshake(HandshakeOptions.NodeReuse); + var h2 = new Handshake(HandshakeOptions.NodeReuse); + + h1.GetKey().ShouldBe(h2.GetKey()); + } + } +} + +#endif diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 8c4f6a50167..708e7151a99 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -287,8 +287,12 @@ protected Handshake(HandshakeOptions nodeType, bool includeSessionId, string too // Get session ID if needed (expensive call) int sessionId = 0; - if (includeSessionId) + if (includeSessionId && NativeMethodsShared.IsWindows) { + // On Windows, SessionId differentiates RDP sessions. + // On Unix, getsid() returns the session leader PID which differs per terminal, + // preventing cross-terminal node reuse. Use 0 since Unix doesn't need + // RDP-style session isolation. using var currentProcess = Process.GetCurrentProcess(); sessionId = currentProcess.SessionId; }