diff --git a/src/Build.UnitTests/BackEnd/TaskHostNodeKey_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostNodeKey_Tests.cs
new file mode 100644
index 00000000000..c02ea2c094e
--- /dev/null
+++ b/src/Build.UnitTests/BackEnd/TaskHostNodeKey_Tests.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Build.Internal;
+using Shouldly;
+using Xunit;
+
+namespace Microsoft.Build.Engine.UnitTests.BackEnd
+{
+ ///
+ /// Tests for TaskHostNodeKey record struct functionality.
+ ///
+ public class TaskHostNodeKey_Tests
+ {
+ [Fact]
+ public void TaskHostNodeKey_Equality_SameValues_AreEqual()
+ {
+ var key1 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+ var key2 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+
+ key1.ShouldBe(key2);
+ (key1 == key2).ShouldBeTrue();
+ key1.GetHashCode().ShouldBe(key2.GetHashCode());
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_Equality_DifferentNodeId_AreNotEqual()
+ {
+ var key1 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+ var key2 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 2);
+
+ key1.ShouldNotBe(key2);
+ (key1 != key2).ShouldBeTrue();
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_Equality_DifferentHandshakeOptions_AreNotEqual()
+ {
+ var key1 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+ var key2 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.X64, 1);
+
+ key1.ShouldNotBe(key2);
+ (key1 != key2).ShouldBeTrue();
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_CanBeUsedAsDictionaryKey()
+ {
+ var dict = new System.Collections.Generic.Dictionary();
+ var key1 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+ var key2 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.X64, 2);
+
+ dict[key1] = "value1";
+ dict[key2] = "value2";
+
+ dict[key1].ShouldBe("value1");
+ dict[key2].ShouldBe("value2");
+
+ // Create a new key with same values as key1
+ var key1Copy = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1);
+ dict[key1Copy].ShouldBe("value1");
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_LargeNodeId_Works()
+ {
+ // Test that we can use node IDs greater than 255 (the previous limit)
+ var key1 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 256);
+ var key2 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, 1000);
+ var key3 = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, int.MaxValue);
+
+ key1.NodeId.ShouldBe(256);
+ key2.NodeId.ShouldBe(1000);
+ key3.NodeId.ShouldBe(int.MaxValue);
+
+ // Ensure they are all different
+ key1.ShouldNotBe(key2);
+ key2.ShouldNotBe(key3);
+ key1.ShouldNotBe(key3);
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_NegativeNodeId_Works()
+ {
+ // Traditional multi-proc builds use -1 for node ID
+ var key = new TaskHostNodeKey(HandshakeOptions.TaskHost | HandshakeOptions.NET, -1);
+
+ key.NodeId.ShouldBe(-1);
+ key.HandshakeOptions.ShouldBe(HandshakeOptions.TaskHost | HandshakeOptions.NET);
+ }
+
+ [Fact]
+ public void TaskHostNodeKey_AllHandshakeOptions_Work()
+ {
+ // Test various HandshakeOptions combinations
+ HandshakeOptions[] optionsList =
+ [
+ HandshakeOptions.None,
+ HandshakeOptions.TaskHost,
+ HandshakeOptions.TaskHost | HandshakeOptions.NET,
+ HandshakeOptions.TaskHost | HandshakeOptions.X64,
+ HandshakeOptions.TaskHost | HandshakeOptions.NET | HandshakeOptions.NodeReuse,
+ HandshakeOptions.TaskHost | HandshakeOptions.CLR2,
+ HandshakeOptions.TaskHost | HandshakeOptions.Arm64
+ ];
+
+ foreach (var options in optionsList)
+ {
+ var key = new TaskHostNodeKey(options, 42);
+
+ key.HandshakeOptions.ShouldBe(options);
+ key.NodeId.ShouldBe(42);
+ }
+ }
+ }
+}
diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs
index 4741ebbbb08..baff1943fa8 100644
--- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs
+++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs
@@ -81,11 +81,19 @@ internal class NodeProviderOutOfProcTaskHost : NodeProviderOutOfProcBase, INodeP
///
/// A mapping of all the task host nodes managed by this provider.
+ /// The key is a TaskHostNodeKey combining HandshakeOptions and scheduled node ID.
///
- private ConcurrentDictionary _nodeContexts;
+ private ConcurrentDictionary _nodeContexts;
+
+ ///
+ /// Reverse mapping from communication node ID to TaskHostNodeKey.
+ /// Used for O(1) lookup when handling node termination from ShutdownAllNodes.
+ ///
+ private ConcurrentDictionary _nodeIdToNodeKey;
///
/// A mapping of all of the INodePacketFactories wrapped by this provider.
+ /// Keyed by the communication node ID (NodeContext.NodeId) for O(1) packet routing.
/// Thread-safe to support parallel taskhost creation in /mt mode where multiple thread nodes
/// can simultaneously create their own taskhosts.
///
@@ -93,16 +101,23 @@ internal class NodeProviderOutOfProcTaskHost : NodeProviderOutOfProcBase, INodeP
///
/// A mapping of all of the INodePacketHandlers wrapped by this provider.
+ /// Keyed by the communication node ID (NodeContext.NodeId) for O(1) packet routing.
/// Thread-safe to support parallel taskhost creation in /mt mode where multiple thread nodes
/// can simultaneously create their own taskhosts.
///
private ConcurrentDictionary _nodeIdToPacketHandler;
///
- /// Keeps track of the set of nodes for which we have not yet received shutdown notification.
+ /// Keeps track of the set of node IDs for which we have not yet received shutdown notification.
///
private HashSet _activeNodes;
+ ///
+ /// Counter for generating unique communication node IDs.
+ /// Incremented atomically for each new node created.
+ ///
+ private int _nextNodeId;
+
///
/// Packet factory we use if there's not already one associated with a particular context.
///
@@ -169,12 +184,23 @@ public IList CreateNodes(int nextNodeId, INodePacketFactory packetFact
///
/// Sends data to the specified node.
+ /// Note: For task hosts, use the overload that takes TaskHostNodeKey instead.
///
/// The node to which data shall be sent.
/// The packet to send.
public void SendData(int nodeId, INodePacket packet)
{
- ErrorUtilities.VerifyThrow(_nodeContexts.TryGetValue(nodeId, out NodeContext context), "Invalid host context specified: {0}.", nodeId);
+ throw new NotImplementedException("For task hosts, use the overload that takes TaskHostNodeKey.");
+ }
+
+ ///
+ /// Sends data to the specified task host node.
+ ///
+ /// The task host node key identifying the target node.
+ /// The packet to send.
+ internal void SendData(TaskHostNodeKey nodeKey, INodePacket packet)
+ {
+ ErrorUtilities.VerifyThrow(_nodeContexts.TryGetValue(nodeKey, out NodeContext context), "Invalid host context specified: {0}.", nodeKey);
SendData(context, packet);
}
@@ -211,10 +237,12 @@ public void ShutdownAllNodes()
public void InitializeComponent(IBuildComponentHost host)
{
this.ComponentHost = host;
- _nodeContexts = new ConcurrentDictionary();
+ _nodeContexts = new ConcurrentDictionary();
+ _nodeIdToNodeKey = new ConcurrentDictionary();
_nodeIdToPacketFactory = new ConcurrentDictionary();
_nodeIdToPacketHandler = new ConcurrentDictionary();
- _activeNodes = new HashSet();
+ _activeNodes = [];
+ _nextNodeId = 0;
_noNodesActiveEvent = new ManualResetEvent(true);
_localPacketFactory = new NodePacketFactory();
@@ -569,17 +597,16 @@ private static string GetPathFromEnvironmentOrDefault(string environmentVariable
/// Make sure a node in the requested context exists.
///
internal bool AcquireAndSetUpHost(
- HandshakeOptions hostContext,
- int taskHostNodeId,
+ TaskHostNodeKey nodeKey,
INodePacketFactory factory,
INodePacketHandler handler,
TaskHostConfiguration configuration,
in TaskHostParameters taskHostParameters)
{
bool nodeCreationSucceeded;
- if (!_nodeContexts.ContainsKey(taskHostNodeId))
+ if (!_nodeContexts.ContainsKey(nodeKey))
{
- nodeCreationSucceeded = CreateNode(hostContext, taskHostNodeId, factory, handler, configuration, taskHostParameters);
+ nodeCreationSucceeded = CreateNode(nodeKey, factory, handler, configuration, taskHostParameters);
}
else
{
@@ -589,9 +616,10 @@ internal bool AcquireAndSetUpHost(
if (nodeCreationSucceeded)
{
- NodeContext context = _nodeContexts[taskHostNodeId];
- _nodeIdToPacketFactory[taskHostNodeId] = factory;
- _nodeIdToPacketHandler[taskHostNodeId] = handler;
+ NodeContext context = _nodeContexts[nodeKey];
+ // Map the transport ID directly to the handlers for O(1) packet routing
+ _nodeIdToPacketFactory[context.NodeId] = factory;
+ _nodeIdToPacketHandler[context.NodeId] = handler;
// Configure the node.
context.SendData(configuration);
@@ -604,10 +632,12 @@ internal bool AcquireAndSetUpHost(
///
/// Expected to be called when TaskHostTask is done with host of the given context.
///
- internal void DisconnectFromHost(int nodeId)
+ internal void DisconnectFromHost(TaskHostNodeKey nodeKey)
{
- bool successRemoveFactory = _nodeIdToPacketFactory.TryRemove(nodeId, out _);
- bool successRemoveHandler = _nodeIdToPacketHandler.TryRemove(nodeId, out _);
+ ErrorUtilities.VerifyThrow(_nodeContexts.TryGetValue(nodeKey, out NodeContext context), "Node context not found for key: {0}. Was the node created?", nodeKey);
+
+ bool successRemoveFactory = _nodeIdToPacketFactory.TryRemove(context.NodeId, out _);
+ bool successRemoveHandler = _nodeIdToPacketHandler.TryRemove(context.NodeId, out _);
ErrorUtilities.VerifyThrow(successRemoveFactory && successRemoveHandler, "Why are we trying to disconnect from a context that we already disconnected from? Did we call DisconnectFromHost twice?");
}
@@ -615,14 +645,22 @@ internal void DisconnectFromHost(int nodeId)
///
/// Instantiates a new MSBuild or MSBuildTaskHost process acting as a child node.
///
- internal bool CreateNode(HandshakeOptions hostContext, int taskHostNodeId, INodePacketFactory factory, INodePacketHandler handler, TaskHostConfiguration configuration, in TaskHostParameters taskHostParameters)
+ internal bool CreateNode(TaskHostNodeKey nodeKey, INodePacketFactory factory, INodePacketHandler handler, TaskHostConfiguration configuration, in TaskHostParameters taskHostParameters)
{
ErrorUtilities.VerifyThrowArgumentNull(factory);
- ErrorUtilities.VerifyThrow(!_nodeIdToPacketFactory.ContainsKey(taskHostNodeId), "We should not already have a factory for this context! Did we forget to call DisconnectFromHost somewhere?");
+ ErrorUtilities.VerifyThrow(!_nodeContexts.ContainsKey(nodeKey), "We should not already have a node for this context! Did we forget to call DisconnectFromHost somewhere?");
+
+ HandshakeOptions hostContext = nodeKey.HandshakeOptions;
// If runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments.
string commandLineArgsPlaceholder = "\"{0}\" /nologo /nodemode:2 /nodereuse:{1} /low:{2} ";
+ // Generate a unique node ID for communication purposes using atomic increment.
+ int communicationNodeId = Interlocked.Increment(ref _nextNodeId);
+
+ // Create callbacks that capture the TaskHostNodeKey
+ void OnNodeContextCreated(NodeContext context) => NodeContextCreated(context, nodeKey);
+
IList nodeContexts;
// Handle .NET task host context
@@ -639,10 +677,10 @@ internal bool CreateNode(HandshakeOptions hostContext, int taskHostNodeId, INode
nodeContexts = GetNodes(
runtimeHostPath,
string.Format(commandLineArgsPlaceholder, Path.Combine(msbuildAssemblyPath, Constants.MSBuildAssemblyName), NodeReuseIsEnabled(hostContext), ComponentHost.BuildParameters.LowPriority),
- taskHostNodeId,
+ communicationNodeId,
this,
handshake,
- NodeContextCreated,
+ OnNodeContextCreated,
NodeContextTerminated,
1);
@@ -663,10 +701,10 @@ internal bool CreateNode(HandshakeOptions hostContext, int taskHostNodeId, INode
nodeContexts = GetNodes(
msbuildLocation,
string.Format(commandLineArgsPlaceholder, string.Empty, NodeReuseIsEnabled(hostContext), ComponentHost.BuildParameters.LowPriority),
- taskHostNodeId,
+ communicationNodeId,
this,
new Handshake(hostContext),
- NodeContextCreated,
+ OnNodeContextCreated,
NodeContextTerminated,
1);
@@ -687,9 +725,10 @@ bool NodeReuseIsEnabled(HandshakeOptions hostContext)
///
/// Method called when a context created.
///
- private void NodeContextCreated(NodeContext context)
+ private void NodeContextCreated(NodeContext context, TaskHostNodeKey nodeKey)
{
- _nodeContexts[context.NodeId] = context;
+ _nodeContexts[nodeKey] = context;
+ _nodeIdToNodeKey[context.NodeId] = nodeKey;
// Start the asynchronous read.
context.BeginAsyncPacketRead();
@@ -702,19 +741,20 @@ private void NodeContextCreated(NodeContext context)
}
///
- /// Method called when a context terminates.
+ /// Method called when a context terminates (called from CreateNode callbacks or ShutdownAllNodes).
///
private void NodeContextTerminated(int nodeId)
{
- _nodeContexts.TryRemove(nodeId, out _);
+ // Remove from nodeKey-based lookup if we have it
+ if (_nodeIdToNodeKey.TryRemove(nodeId, out TaskHostNodeKey nodeKey))
+ {
+ _nodeContexts.TryRemove(nodeKey, out _);
+ }
// May also be removed by unnatural termination, so don't assume it's there
lock (_activeNodes)
{
- if (_activeNodes.Contains(nodeId))
- {
- _activeNodes.Remove(nodeId);
- }
+ _activeNodes.Remove(nodeId);
if (_activeNodes.Count == 0)
{
diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs
index abb36afecd2..9bf09a47b11 100644
--- a/src/Build/Instance/TaskFactories/TaskHostTask.cs
+++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs
@@ -28,12 +28,6 @@ namespace Microsoft.Build.BackEnd
///
internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactory, INodePacketHandler
{
- private const int HANDSHAKE_OPTIONS_BITS = 9;
-
- private const int HANDSHAKE_OPTIONS_MASK = 0x1FF;
-
- private const int NODE_ID_MAX_VALUE_FOR_MULTITHREADED = 255;
-
///
/// The IBuildEngine callback object.
///
@@ -100,9 +94,9 @@ internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactor
private HandshakeOptions _requiredContext = HandshakeOptions.None;
///
- /// The task host node ID of the task host we're launching.
+ /// The task host node key identifying the task host we're launching.
///
- private int _taskHostNodeId;
+ private TaskHostNodeKey _taskHostNodeKey;
///
/// The ID of the node on which this task is scheduled to run.
@@ -278,7 +272,7 @@ public void Cancel()
{
if (_taskHostProvider != null && _connectedToTaskHost)
{
- _taskHostProvider.SendData(_taskHostNodeId, new TaskHostTaskCancelled());
+ _taskHostProvider.SendData(_taskHostNodeKey, new TaskHostTaskCancelled());
}
}
@@ -347,8 +341,8 @@ public bool Execute()
nodeReuse: _buildComponentHost.BuildParameters.EnableNodeReuse && _useSidecarTaskHost,
taskHostParameters: _taskHostParameters);
- _taskHostNodeId = GenerateTaskHostNodeId(_scheduledNodeId, _requiredContext);
- _connectedToTaskHost = _taskHostProvider.AcquireAndSetUpHost(_requiredContext, _taskHostNodeId, this, this, hostConfiguration, _taskHostParameters);
+ _taskHostNodeKey = new TaskHostNodeKey(_requiredContext, _scheduledNodeId);
+ _connectedToTaskHost = _taskHostProvider.AcquireAndSetUpHost(_taskHostNodeKey, this, this, hostConfiguration, _taskHostParameters);
}
if (_connectedToTaskHost)
@@ -377,7 +371,7 @@ public bool Execute()
{
lock (_taskHostLock)
{
- _taskHostProvider.DisconnectFromHost(_taskHostNodeId);
+ _taskHostProvider.DisconnectFromHost(_taskHostNodeKey);
_connectedToTaskHost = false;
}
}
@@ -399,22 +393,6 @@ public bool Execute()
return _taskExecutionSucceeded;
}
- private static int GenerateTaskHostNodeId(int scheduledNodeId, HandshakeOptions handshakeOptions)
- {
- // For traditional multi-proc builds, the task host id is just (int)HandshakeOptions that represents the runtime / architecture.
- // For new multi-threaded mode, NodeProviderOutOfProcTaskHost needs to distinguish task hosts not only by HandshakeOptions (runtime / architecture),
- // but also by which node they were requested. This is because NodeProviderOutOfProcTaskHost runs on the same process as multiple in-proc nodes,
- // as opposed to the traditional multi-proc case where each node and single NodeProviderOutOfProcTaskHost runs on its own process.
- // nodeId: [1, 255] (8 bits more than enough) (max is number of processors, usually 8. Let's assume max is 256 processors)
- // HandshakeOptions: [0, 511] (9 bits)
- // Pack nodeId into upper bits, handshakeOptions into lower bits
- ErrorUtilities.VerifyThrowArgumentOutOfRange(scheduledNodeId == -1 || (scheduledNodeId >= 1 && scheduledNodeId <= NODE_ID_MAX_VALUE_FOR_MULTITHREADED), nameof(scheduledNodeId));
-
- return scheduledNodeId == -1 ?
- (int)handshakeOptions :
- (scheduledNodeId << HANDSHAKE_OPTIONS_BITS) | ((int)handshakeOptions & HANDSHAKE_OPTIONS_MASK);
- }
-
///
/// Registers the specified handler for a particular packet type.
///
diff --git a/src/MSBuild.UnitTests/XMake_Tests.cs b/src/MSBuild.UnitTests/XMake_Tests.cs
index 4173c747739..f4eafd483a6 100644
--- a/src/MSBuild.UnitTests/XMake_Tests.cs
+++ b/src/MSBuild.UnitTests/XMake_Tests.cs
@@ -2935,24 +2935,6 @@ public void TasksGetAssemblyLoadContexts()
#endif
- [Fact]
- public void ThrowsWhenMaxCpuCountTooLargeForMultiThreadedAndForceAllTasksOutOfProc()
- {
- string projectContent = """
-
-
- """;
- using TestEnvironment testEnvironment = TestEnvironment.Create();
- testEnvironment.SetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC", "1");
- string project = testEnvironment.CreateTestProjectWithFiles("project.proj", projectContent).ProjectFile;
-
-#if FEATURE_GET_COMMANDLINE
- MSBuildApp.Execute(@"c:\bin\msbuild.exe " + project + " / m:257 /mt").ShouldBe(MSBuildApp.ExitType.SwitchError);
-#else
- MSBuildApp.Execute(new[] { @"c:\bin\msbuild.exe", project, "/m:257 /mt" }).ShouldBe(MSBuildApp.ExitType.SwitchError);
-#endif
- }
-
private string CopyMSBuild()
{
string dest = null;
diff --git a/src/MSBuild/Resources/Strings.resx b/src/MSBuild/Resources/Strings.resx
index bf1e7bc6020..701bb681d2c 100644
--- a/src/MSBuild/Resources/Strings.resx
+++ b/src/MSBuild/Resources/Strings.resx
@@ -1707,7 +1707,4 @@
Don't forget to update this comment after using the new code.
-->
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
-
diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf
index d946f2eb8ce..c050d60e03e 100644
--- a/src/MSBuild/Resources/xlf/Strings.cs.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf
@@ -1524,11 +1524,6 @@ Když se nastaví na MessageUponIsolationViolation (nebo jeho krátký
Protokoly MSBuild a informace o ladění budou dostupné v „{0}“
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- V případě zapnutého režimu s více vlákny a MSBUILDFORCEALLTASKSOUTOFPROC=1 nemůže být maxCpuCount větší než {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: Je nutné zadat název funkce pro přepínač featureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf
index 0cba6805f2c..30794b9ef29 100644
--- a/src/MSBuild/Resources/xlf/Strings.de.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.de.xlf
@@ -1512,11 +1512,6 @@ Hinweis: Ausführlichkeit der Dateiprotokollierungen
MSBuild-Protokolle und Debuginformationen befinden sich auf "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Bei aktiviertem MultiThreaded-Modus und MSBUILDFORCEALLTASKSOUTOFPROC=1 darf maxCpuCount nicht größer als {0} sein.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: Es muss ein Featurename für den featureAvailability-Schalter angegeben werden.
diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf
index 14f75ad5b07..a96c9ca8c11 100644
--- a/src/MSBuild/Resources/xlf/Strings.es.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.es.xlf
@@ -1518,11 +1518,6 @@ Esta marca es experimental y puede que no funcione según lo previsto.
Los registros de MSBuild y la información de depuración estarán en "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Con el modo multiThreaded habilitado y MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount no puede ser mayor que {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: debe proporcionar un nombre de característica para el conmutador featureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf
index 58de9b0a22a..cca6ab9830e 100644
--- a/src/MSBuild/Resources/xlf/Strings.fr.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf
@@ -1512,11 +1512,6 @@ Remarque : verbosité des enregistreurs d’événements de fichiers
Les journaux MSBuild et les informations de débogage seront au "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Avec le mode multithread activé et MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount ne peut pas être supérieur à {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: doit fournir un nom de fonctionnalité pour le commutateur featureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf
index 4aa3620e44c..26f3d724edb 100644
--- a/src/MSBuild/Resources/xlf/Strings.it.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.it.xlf
@@ -1523,11 +1523,6 @@ Nota: livello di dettaglio dei logger di file
I log e le informazioni di debug di MSBuild sono contenuti in "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Con la modalità multithread attivata e MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount non può essere maggiore di {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: È necessario fornire un nome funzionalità per l’opzione featureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf
index de62073f1eb..5d1f7dacd88 100644
--- a/src/MSBuild/Resources/xlf/Strings.ja.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf
@@ -1512,11 +1512,6 @@
MSBuild のログとデバッグ情報は、"{0}" にあります。
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- マルチスレッド モードが有効で、MSBUILDFORCEALLTASKSOUTOFPROC=1 の場合、maxCpuCount は {0} を超えることはできません。
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: featureAvailability スイッチの機能名を指定する必要があります。
diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf
index 54da373cdfe..b5691e61719 100644
--- a/src/MSBuild/Resources/xlf/Strings.ko.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf
@@ -1513,11 +1513,6 @@
MSBuild 로그 및 디버그 정보는 "{0}"에 있습니다.
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- 다중 스레드 모드가 켜져 있고 MSBUILDFORCEALLTASKSOUTOFPROC=1인 경우, maxCpuCount는 {0}보다 클 수 없습니다..
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: featureAvailability 스위치에 대한 기능 이름을 제공해야 합니다.
diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf
index a6cb09da145..03b0e01f39c 100644
--- a/src/MSBuild/Resources/xlf/Strings.pl.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf
@@ -1521,11 +1521,6 @@ Ta flaga jest eksperymentalna i może nie działać zgodnie z oczekiwaniami.
Dzienniki i informacje debugowania programu MSBuild będą znajdować się w lokalizacji „{0}”
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Przy włączonym trybie wielowątkowym i MSBUILDFORCEALLTASKSOUTOFPROC=1 wartość maxCpuCount nie może być większa niż {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: należy podać nazwę funkcji dla przełącznika dostępności funkcji.
diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf
index b9483ee66be..76cbbebd996 100644
--- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf
@@ -1512,11 +1512,6 @@ arquivo de resposta.
Os logs e as informações de depuração do MSBuild estarão no "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Com o modo multiThreaded ativado e MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount não poderá ser maior que {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: É necessário fornecer um nome de recurso para a chave featureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf
index bf1d012d0e9..cec4de53558 100644
--- a/src/MSBuild/Resources/xlf/Strings.ru.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf
@@ -1510,11 +1510,6 @@
Журналы MSBuild и отладочные сведения будут доступны по адресу "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- Если включён многопоточный режим и MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount не может быть больше {0}.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: необходимо указать имя функции для переключателя FeatureAvailability.
diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf
index 0a9e4024bc6..940f30c6aa8 100644
--- a/src/MSBuild/Resources/xlf/Strings.tr.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf
@@ -1515,11 +1515,6 @@
MSBuild günlükleri ve hata ayıklama bilgileri "{0}" yolunda olacak
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- multiThreaded modu açık ve MSBUILDFORCEALLTASKSOUTOFPROC=1 olduğunda, maxCpuCount değeri {0} değerinden büyük olamaz.
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: featureAvailability anahtarı için özellik adı belirtilmelidir.
diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf
index 79fe8567567..f9cd58c592f 100644
--- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf
@@ -1512,11 +1512,6 @@
MSBuild 日志和调试信息将位于"{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- 在多线程模式开启且 MSBUILDFORCEALLTASKSOUTOFPROC=1 的情况下,maxCpuCount 不能大于 {0}。
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: 必须为 featureAvailability 开关提供功能名称。
diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf
index 87546d3eed0..3bb145115b7 100644
--- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf
@@ -1512,11 +1512,6 @@
MSBuild 記錄和偵錯工具資訊將位於 "{0}"
-
- With multiThreaded mode on and MSBUILDFORCEALLTASKSOUTOFPROC=1, maxCpuCount cannot be greater that {0}.
- 開啟多執行緒模式且 MSBUILDFORCEALLTASKSOUTOFPROC=1 時,maxCpuCount 不能大於 {0}。
-
- MSBUILD : error MSB1067: Must provide a feature name for the featureAvailability switch.MSBUILD : error MSB1067: 必須提供 featureAvailability 切換的功能名稱。
diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs
index f6bb284bbe3..0bc9bd7aafd 100644
--- a/src/MSBuild/XMake.cs
+++ b/src/MSBuild/XMake.cs
@@ -1259,8 +1259,6 @@ private static void ResetGatheringSwitchesState()
///
private static uint? s_originalConsoleMode = null;
- private const int MAX_MULTITHREADED_CPU_COUNT_FOR_TASK_HOST = 256;
-
///
/// Initializes the build engine, and starts the project building.
///
@@ -1311,12 +1309,6 @@ internal static bool BuildProject(
string[] commandLine)
#endif
{
- // Set limitation for multithreaded and MSBUILDFORCEALLTASKSOUTOFPROC=1. Max is 256 because of unique task host id generation.
- if (multiThreaded && Traits.Instance.ForceAllTasksOutOfProcToTaskHost)
- {
- ErrorUtilities.VerifyThrowArgument(cpuCount <= MAX_MULTITHREADED_CPU_COUNT_FOR_TASK_HOST, "MaxCpuCountTooLargeForMultiThreadedAndForceAllTasksOutOfProc", MAX_MULTITHREADED_CPU_COUNT_FOR_TASK_HOST);
- }
-
if (FileUtilities.IsVCProjFilename(projectFile) || FileUtilities.IsDspFilename(projectFile))
{
InitializationException.Throw(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("XMake.ProjectUpgradeNeededToVcxProj", projectFile), null);
diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs
index 76de01615cc..f17d95b612e 100644
--- a/src/Shared/CommunicationsUtilities.cs
+++ b/src/Shared/CommunicationsUtilities.cs
@@ -37,7 +37,6 @@ namespace Microsoft.Build.Internal
///
/// Enumeration of all possible (currently supported) options for handshakes.
///
- /// In case of adding new options, please remember to update the generation of unique task host node id in NodeProviderOutOfProcTaskHost.
[Flags]
internal enum HandshakeOptions
{
@@ -89,6 +88,19 @@ internal enum HandshakeOptions
SidecarTaskHost = 256,
}
+ ///
+ /// Represents a unique key for identifying task host nodes.
+ /// Combines HandshakeOptions (which specify runtime/architecture configuration) with
+ /// the scheduled node ID to uniquely identify task hosts in multi-threaded mode.
+ ///
+ /// The handshake options specifying runtime and architecture configuration.
+ ///
+ /// The scheduled node ID. In traditional multi-proc builds, this is -1 (meaning the task host
+ /// is identified by HandshakeOptions alone). In multi-threaded mode, each in-proc node has
+ /// its own task host, so the node ID is used to distinguish them.
+ ///
+ internal readonly record struct TaskHostNodeKey(HandshakeOptions HandshakeOptions, int NodeId);
+
///
/// Status codes for the handshake process.
/// It aggregates return values across several functions so we use an aggregate instead of a separate class for each method.