| File: BackEnd\Components\RequestBuilder\TaskRouter.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Concurrent; namespace Microsoft.Build.BackEnd { /// <summary> /// Determines where a task should be executed in multi-threaded mode. /// In multi-threaded execution mode, tasks implementing IMultiThreadableTask or marked with /// MSBuildMultiThreadableTaskAttribute run in-process within thread nodes, while legacy tasks /// are routed to sidecar TaskHost processes for isolation. /// </summary> /// <remarks> /// This class should only be used when in multi-threaded mode. Traditional multi-proc builds /// have different semantics and should not use this routing logic. /// </remarks> internal static class TaskRouter { /// <summary> /// Cache of task types to their multi-threadable attribute status. /// This avoids repeated reflection calls for the same task types. /// </summary> private static readonly ConcurrentDictionary<Type, bool> s_multiThreadableTaskCache = new(); /// <summary> /// Determines if a task needs to be routed to an out-of-process TaskHost sidecar /// in multi-threaded mode based on its thread-safety characteristics. /// </summary> /// <param name="taskType">The type of the task to evaluate.</param> /// <returns> /// True if the task should be executed in an out-of-process TaskHost sidecar; /// false if it can safely run in-process within a thread node. /// </returns> /// <remarks> /// This method only considers the task's thread-safety indicators. /// The caller is responsible for: /// - Only calling this in multi-threaded mode /// - Handling explicit out-of-proc requests (via TaskHostFactory or parameters) /// - Handling the isAlreadyOutOfProc scenario /// /// In multi-threaded mode: /// - Tasks marked with MSBuildMultiThreadableTaskAttribute (non-inheritable) are considered /// thread-safe and can run in-process (returns false) /// - Tasks without the attribute must run in a sidecar TaskHost for isolation (returns true) /// </remarks> public static bool NeedsTaskHostInMultiThreadedMode(Type taskType) { ArgumentNullException.ThrowIfNull(taskType); // Tasks without the thread-safety attribute need isolation in a TaskHost sidecar return !HasMultiThreadableTaskAttribute(taskType); } /// <summary> /// Checks if a task type is marked with MSBuildMultiThreadableTaskAttribute. /// Detection is based on namespace and name only, ignoring the defining assembly, /// which allows customers to define the attribute in their own assemblies. /// Results are cached to avoid repeated reflection calls. /// </summary> /// <param name="taskType">The task type to check.</param> /// <returns>True if the task has the attribute; false otherwise.</returns> private static bool HasMultiThreadableTaskAttribute(Type taskType) { return s_multiThreadableTaskCache.GetOrAdd( taskType, static t => { const string attributeFullName = "Microsoft.Build.Framework.MSBuildMultiThreadableTaskAttribute"; // Check for the attribute by full name, not by type identity // This allows custom-defined attributes from different assemblies foreach (object attr in t.GetCustomAttributes(inherit: false)) { if (attr.GetType().FullName == attributeFullName) { return true; } } return false; }); } /// <summary> /// Clears the thread-safety cache. Used primarily for testing. /// </summary> internal static void ClearCache() { s_multiThreadableTaskCache.Clear(); } } }