Skip to content

Commit ce41c05

Browse files
authored
/mt implies inline task factories out of proc (#12614)
1 parent 082d677 commit ce41c05

15 files changed

Lines changed: 624 additions & 65 deletions

File tree

src/Build.UnitTests/BackEnd/TaskRegistry_Tests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ public void TaskFactoryWithNullTaskTypeLogsError()
12441244

12451245
TaskRegistry registry = CreateTaskRegistryAndRegisterTasks(elementList);
12461246

1247-
InvalidProjectFileException exception = Should.Throw<InvalidProjectFileException>(() => registry.GetRegisteredTask("Task1", "none", null, false, new TargetLoggingContext(_loggingService, new BuildEventContext(1, 1, BuildEventContext.InvalidProjectContextId, 1)), ElementLocation.Create("none", 1, 2)));
1247+
InvalidProjectFileException exception = Should.Throw<InvalidProjectFileException>(() => registry.GetRegisteredTask("Task1", "none", null, false, new TargetLoggingContext(_loggingService, new BuildEventContext(1, 1, BuildEventContext.InvalidProjectContextId, 1)), ElementLocation.Create("none", 1, 2), false));
12481248

12491249
exception.ErrorCode.ShouldBe("MSB4175");
12501250

@@ -2008,7 +2008,7 @@ private void RetrieveAndValidateRegisteredTaskRecord(
20082008
string expectedArchitecture)
20092009
{
20102010
bool retrievedFromCache;
2011-
var record = registry.GetTaskRegistrationRecord(TestTaskName, null, taskParameters, exactMatchRequired, _targetLoggingContext, _elementLocation, out retrievedFromCache);
2011+
var record = registry.GetTaskRegistrationRecord(TestTaskName, null, taskParameters, exactMatchRequired, _targetLoggingContext, _elementLocation, out retrievedFromCache, false);
20122012

20132013
if (shouldBeRetrieved)
20142014
{

src/Build/BackEnd/BuildManager/BuildManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ public void EndBuild()
11491149
Reset();
11501150
_buildManagerState = BuildManagerState.Idle;
11511151

1152-
if (Traits.Instance.ForceTaskFactoryOutOfProc)
1152+
if (Traits.Instance.ForceTaskFactoryOutOfProc || _buildParameters.MultiThreaded)
11531153
{
11541154
TaskFactoryUtilities.CleanCurrentProcessInlineTaskDirectory();
11551155
}

src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -890,18 +890,18 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary<string, string> taskId
890890
{
891891
if (!_intrinsicTasks.TryGetValue(_taskName, out TaskFactoryWrapper returnClass))
892892
{
893-
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, true /* exact match */, _targetLoggingContext, _taskLocation);
893+
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, true /* exact match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);
894894
if (returnClass == null)
895895
{
896-
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, false /* fuzzy match */, _targetLoggingContext, _taskLocation);
896+
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, taskIdentityParameters, false /* fuzzy match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);
897897

898898
if (returnClass == null)
899899
{
900-
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, true /* exact match */, _targetLoggingContext, _taskLocation);
900+
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, true /* exact match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);
901901

902902
if (returnClass == null)
903903
{
904-
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, false /* fuzzy match */, _targetLoggingContext, _taskLocation);
904+
returnClass = _projectInstance.TaskRegistry.GetRegisteredTask(_taskName, null, null, false /* fuzzy match */, _targetLoggingContext, _taskLocation, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false);
905905

906906
if (returnClass == null)
907907
{
@@ -980,13 +980,17 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
980980
}
981981
else
982982
{
983-
TaskFactoryLoggingHost loggingHost = new TaskFactoryLoggingHost(_buildEngine.IsRunningMultipleNodes, _taskLocation, _taskLoggingContext);
983+
TaskFactoryEngineContext taskFactoryEngineContext = new TaskFactoryEngineContext(_buildEngine.IsRunningMultipleNodes, _taskLocation, _taskLoggingContext, _buildComponentHost?.BuildParameters?.MultiThreaded ?? false, Traits.Instance.ForceTaskFactoryOutOfProc);
984984
bool isTaskHost = false;
985985
try
986986
{
987987
// Check if we should force out-of-process execution for non-AssemblyTaskFactory instances
988+
// This happens when: 1) Environment variable is set, OR 2) MultiThreaded build is enabled
988989
// IntrinsicTaskFactory tasks run in proc always
989-
if (Traits.Instance.ForceTaskFactoryOutOfProc && _taskFactoryWrapper.TaskFactory is not IntrinsicTaskFactory)
990+
bool shouldRunOutOfProc = TaskFactoryUtilities.ShouldCompileForOutOfProcess(taskFactoryEngineContext)
991+
&& _taskFactoryWrapper.TaskFactory is not IntrinsicTaskFactory;
992+
993+
if (shouldRunOutOfProc)
990994
{
991995
// Custom Task factories are not supported, internal TaskFactories implement this marker interface
992996
if (_taskFactoryWrapper.TaskFactory is not IOutOfProcTaskFactory outOfProcTaskFactory)
@@ -999,15 +1003,15 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
9991003
return null;
10001004
}
10011005

1002-
task = CreateTaskHostTaskForOutOfProcFactory(taskIdentityParameters, loggingHost, outOfProcTaskFactory);
1006+
task = CreateTaskHostTaskForOutOfProcFactory(taskIdentityParameters, taskFactoryEngineContext, outOfProcTaskFactory);
10031007
isTaskHost = true;
10041008
}
10051009
else
10061010
{
10071011
// Normal in-process execution for custom task factories
10081012
task = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ?
1009-
taskFactory2.CreateTask(loggingHost, taskIdentityParameters) :
1010-
_taskFactoryWrapper.TaskFactory.CreateTask(loggingHost);
1013+
taskFactory2.CreateTask(taskFactoryEngineContext, taskIdentityParameters) :
1014+
_taskFactoryWrapper.TaskFactory.CreateTask(taskFactoryEngineContext);
10111015
}
10121016

10131017
// Track telemetry for non-AssemblyTaskFactory task factories
@@ -1016,7 +1020,7 @@ private ITask InstantiateTask(int scheduledNodeId, IDictionary<string, string> t
10161020
finally
10171021
{
10181022
#if FEATURE_APPDOMAIN
1019-
loggingHost.MarkAsInactive();
1023+
taskFactoryEngineContext.MarkAsInactive();
10201024
#endif
10211025
}
10221026
}
@@ -1730,20 +1734,20 @@ private void DisplayCancelWaitMessage()
17301734

17311735
/// <summary>
17321736
/// Creates a <see cref="TaskHostTask"/> wrapper to run a non-AssemblyTaskFactory task out of process.
1733-
/// This is used when Traits.Instance.ForceTaskFactoryOutOfProc is true to ensure
1737+
/// This is used when Traits.Instance.ForceTaskFactoryOutOfProc=1 is true or the multi-threaded mode is active to ensure
17341738
/// non-AssemblyTaskFactory tasks run in isolation.
17351739
/// </summary>
17361740
/// <param name="taskIdentityParameters">Task identity parameters.</param>
1737-
/// <param name="loggingHost">The logging host to use for the task.</param>
1741+
/// <param name="taskFactoryEngineContext">The engine context to use for the task.</param>
17381742
/// <param name="outOfProcTaskFactory">The out-of-process task factory instance.</param>
17391743
/// <returns>A TaskHostTask that will execute the inner task out of process, or <code>null</code> if task creation fails.</returns>
1740-
private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string> taskIdentityParameters, TaskFactoryLoggingHost loggingHost, IOutOfProcTaskFactory outOfProcTaskFactory)
1744+
private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string> taskIdentityParameters, TaskFactoryEngineContext taskFactoryEngineContext, IOutOfProcTaskFactory outOfProcTaskFactory)
17411745
{
17421746
ITask innerTask;
17431747

17441748
innerTask = _taskFactoryWrapper.TaskFactory is ITaskFactory2 taskFactory2 ?
1745-
taskFactory2.CreateTask(loggingHost, taskIdentityParameters) :
1746-
_taskFactoryWrapper.TaskFactory.CreateTask(loggingHost);
1749+
taskFactory2.CreateTask(taskFactoryEngineContext, taskIdentityParameters) :
1750+
_taskFactoryWrapper.TaskFactory.CreateTask(taskFactoryEngineContext);
17471751

17481752
if (innerTask == null)
17491753
{
@@ -1758,7 +1762,8 @@ private ITask CreateTaskHostTaskForOutOfProcFactory(IDictionary<string, string>
17581762
string resolvedAssemblyLocation = outOfProcTaskFactory.GetAssemblyPath();
17591763

17601764
// This should never happen - if the factory can create a task, it should know where the assembly is
1761-
ErrorUtilities.VerifyThrow(!string.IsNullOrEmpty(resolvedAssemblyLocation),
1765+
ErrorUtilities.VerifyThrow(
1766+
!string.IsNullOrEmpty(resolvedAssemblyLocation),
17621767
$"IOutOfProcTaskFactory {_taskFactoryWrapper.TaskFactory.FactoryName} created a task but returned null/empty assembly path");
17631768

17641769
LoadedType taskLoadedType = new LoadedType(

src/Build/Instance/TaskFactoryLoggingHost.cs renamed to src/Build/Instance/TaskFactoryEngineContext.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ namespace Microsoft.Build.BackEnd
2020
/// <summary>
2121
/// The host allows task factories access to method to allow them to log message during the construction of the task factories.
2222
/// </summary>
23-
internal class TaskFactoryLoggingHost :
23+
internal class TaskFactoryEngineContext :
2424
#if FEATURE_APPDOMAIN
2525
MarshalByRefObject,
2626
#endif
27-
IBuildEngine
27+
IBuildEngine,
28+
ITaskFactoryBuildParameterProvider
2829
{
2930
/// <summary>
3031
/// Location of the task node in the original file
@@ -36,6 +37,16 @@ internal class TaskFactoryLoggingHost :
3637
/// </summary>
3738
private BuildLoggingContext _loggingContext;
3839

40+
/// <summary>
41+
/// Whether the build is running in multi-threaded mode.
42+
/// </summary>
43+
private readonly bool _isMultiThreadedBuild;
44+
45+
/// <summary>
46+
/// Whether task factories should be forced to compile for out-of-process execution.
47+
/// </summary>
48+
private readonly bool _forceOutOfProcessExecution;
49+
3950
/// <summary>
4051
/// Is the system running in multi-process mode and requires events to be serializable
4152
/// </summary>
@@ -58,7 +69,7 @@ internal class TaskFactoryLoggingHost :
5869
/// <summary>
5970
/// Constructor
6071
/// </summary>
61-
public TaskFactoryLoggingHost(bool isRunningWithMultipleNodes, ElementLocation elementLocation, BuildLoggingContext loggingContext)
72+
public TaskFactoryEngineContext(bool isRunningWithMultipleNodes, ElementLocation elementLocation, BuildLoggingContext loggingContext, bool isMultiThreadedBuild, bool forceOutOfProcessExecution)
6273
{
6374
ErrorUtilities.VerifyThrowArgumentNull(loggingContext);
6475
ErrorUtilities.VerifyThrowInternalNull(elementLocation);
@@ -67,6 +78,8 @@ public TaskFactoryLoggingHost(bool isRunningWithMultipleNodes, ElementLocation e
6778
_isRunningWithMultipleNodes = isRunningWithMultipleNodes;
6879
_loggingContext = loggingContext;
6980
_elementLocation = elementLocation;
81+
_isMultiThreadedBuild = isMultiThreadedBuild;
82+
_forceOutOfProcessExecution = forceOutOfProcessExecution;
7083
}
7184

7285
/// <summary>
@@ -146,6 +159,37 @@ internal BuildLoggingContext LoggingContext
146159
{ return _loggingContext; }
147160
}
148161

162+
/// <summary>
163+
/// Gets a value indicating whether the build is running in multi-threaded mode (/mt flag).
164+
/// </summary>
165+
/// <remarks>
166+
/// This property implements ITaskFactoryBuildParameterProvider to allow task factories to determine
167+
/// if they should compile for out-of-process execution during their Initialize() method.
168+
/// </remarks>
169+
public bool IsMultiThreadedBuild
170+
{
171+
get
172+
{
173+
VerifyActiveProxy();
174+
return _isMultiThreadedBuild;
175+
}
176+
}
177+
178+
/// <summary>
179+
/// Gets a value indicating whether task factories should be forced to compile for out-of-process execution.
180+
/// </summary>
181+
/// <remarks>
182+
/// This is controlled by the MSBUILDFORCEINLINETASKFACTORIESOUTOFPROC environment variable.
183+
/// </remarks>
184+
public bool ForceOutOfProcessExecution
185+
{
186+
get
187+
{
188+
VerifyActiveProxy();
189+
return _forceOutOfProcessExecution;
190+
}
191+
}
192+
149193
#region IBuildEngine Members
150194

151195
/// <summary>

0 commit comments

Comments
 (0)