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.