Skip to content

Commit 585b873

Browse files
Use hashing for targets list comparison and enable scheduler tests (#11870)
1 parent ed84329 commit 585b873

File tree

2 files changed

+60
-54
lines changed

2 files changed

+60
-54
lines changed

src/Build.UnitTests/BackEnd/Scheduler_Tests.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public void Dispose()
101101
/// <summary>
102102
/// Verify that when a single request is submitted, we get a request assigned back out.
103103
/// </summary>
104-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
104+
[Fact]
105105
public void TestSimpleRequest()
106106
{
107107
CreateConfiguration(1, "foo.proj");
@@ -117,7 +117,7 @@ public void TestSimpleRequest()
117117
/// <summary>
118118
/// Verify that when we submit a request and we already have results, we get the results back.
119119
/// </summary>
120-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
120+
[Fact]
121121
public void TestSimpleRequestWithCachedResultsSuccess()
122122
{
123123
CreateConfiguration(1, "foo.proj");
@@ -141,7 +141,7 @@ public void TestSimpleRequestWithCachedResultsSuccess()
141141
/// <summary>
142142
/// Verify that when we submit a request with failing results, we get the results back.
143143
/// </summary>
144-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
144+
[Fact]
145145
public void TestSimpleRequestWithCachedResultsFail()
146146
{
147147
CreateConfiguration(1, "foo.proj");
@@ -165,7 +165,7 @@ public void TestSimpleRequestWithCachedResultsFail()
165165
/// <summary>
166166
/// Verify that when we submit a child request with results cached, we get those results back.
167167
/// </summary>
168-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
168+
[Fact]
169169
public void TestChildRequest()
170170
{
171171
CreateConfiguration(1, "foo.proj");
@@ -195,7 +195,7 @@ public void TestChildRequest()
195195
/// <summary>
196196
/// Verify that when multiple requests are submitted, the first one in is the first one out.
197197
/// </summary>
198-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
198+
[Fact]
199199
public void TestMultipleRequests()
200200
{
201201
CreateConfiguration(1, "foo.proj");
@@ -213,7 +213,7 @@ public void TestMultipleRequests()
213213
/// <summary>
214214
/// Verify that when multiple requests are submitted with results cached, we get the results back.
215215
/// </summary>
216-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
216+
[Fact]
217217
public void TestMultipleRequestsWithSomeResults()
218218
{
219219
CreateConfiguration(1, "foo.proj");
@@ -235,7 +235,7 @@ public void TestMultipleRequestsWithSomeResults()
235235
/// <summary>
236236
/// Verify that when multiple requests are submitted with results cached, we get the results back.
237237
/// </summary>
238-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
238+
[Fact]
239239
public void TestMultipleRequestsWithAllResults()
240240
{
241241
CreateConfiguration(1, "foo.proj");
@@ -266,7 +266,7 @@ public void TestMultipleRequestsWithAllResults()
266266
/// Verify that if the affinity of one of the requests is out-of-proc, we create an out-of-proc node (but only one)
267267
/// even if the max node count = 1.
268268
/// </summary>
269-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
269+
[Fact]
270270
public void TestOutOfProcNodeCreatedWhenAffinityIsOutOfProc()
271271
{
272272
CreateConfiguration(1, "foo.proj");
@@ -288,7 +288,7 @@ public void TestOutOfProcNodeCreatedWhenAffinityIsOutOfProc()
288288
/// Verify that if the affinity of our requests is out-of-proc, that many out-of-proc nodes will
289289
/// be made (assuming it does not exceed MaxNodeCount)
290290
/// </summary>
291-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
291+
[Fact]
292292
public void TestOutOfProcNodesCreatedWhenAffinityIsOutOfProc()
293293
{
294294
_host.BuildParameters.MaxNodeCount = 4;
@@ -313,7 +313,7 @@ public void TestOutOfProcNodesCreatedWhenAffinityIsOutOfProc()
313313
/// we still won't create any new nodes if they're all for the same configuration --
314314
/// they'd end up all being assigned to the same node.
315315
/// </summary>
316-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
316+
[Fact]
317317
public void TestNoNewNodesCreatedForMultipleRequestsWithSameConfiguration()
318318
{
319319
_host.BuildParameters.MaxNodeCount = 3;
@@ -336,7 +336,7 @@ public void TestNoNewNodesCreatedForMultipleRequestsWithSameConfiguration()
336336
/// Verify that if the affinity of our requests is "any", we will not create more than
337337
/// MaxNodeCount nodes (1 IP node + MaxNodeCount - 1 OOP nodes)
338338
/// </summary>
339-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
339+
[Fact]
340340
public void TestMaxNodeCountNotExceededWithRequestsOfAffinityAny()
341341
{
342342
_host.BuildParameters.MaxNodeCount = 3;
@@ -366,7 +366,7 @@ public void TestMaxNodeCountNotExceededWithRequestsOfAffinityAny()
366366
/// node will service an Any request instead of an inproc request, leaving only one non-inproc request for the second round
367367
/// of node creation.
368368
/// </summary>
369-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
369+
[Fact]
370370
public void VerifyRequestOrderingDoesNotAffectNodeCreationCountWithInProcAndAnyRequests()
371371
{
372372
// Since we're creating our own BuildManager, we need to make sure that the default
@@ -414,7 +414,7 @@ public void VerifyRequestOrderingDoesNotAffectNodeCreationCountWithInProcAndAnyR
414414
/// Verify that if the affinity of our requests is out-of-proc, we will create as many as
415415
/// MaxNodeCount out-of-proc nodes
416416
/// </summary>
417-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
417+
[Fact]
418418
public void TestMaxNodeCountOOPNodesCreatedForOOPAffinitizedRequests()
419419
{
420420
_host.BuildParameters.MaxNodeCount = 3;
@@ -444,7 +444,7 @@ public void TestMaxNodeCountOOPNodesCreatedForOOPAffinitizedRequests()
444444
/// is less than MaxNodeCount, that we only create MaxNodeCount - 1 OOP nodes (for a total of MaxNodeCount
445445
/// nodes, when the inproc node is included)
446446
/// </summary>
447-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
447+
[Fact]
448448
public void TestMaxNodeCountNodesNotExceededWithSomeOOPRequests1()
449449
{
450450
_host.BuildParameters.MaxNodeCount = 3;
@@ -474,7 +474,7 @@ public void TestMaxNodeCountNodesNotExceededWithSomeOOPRequests1()
474474
/// is less than MaxNodeCount, that we only create MaxNodeCount - 1 OOP nodes (for a total of MaxNodeCount
475475
/// nodes, when the inproc node is included)
476476
/// </summary>
477-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
477+
[Fact]
478478
public void TestMaxNodeCountNodesNotExceededWithSomeOOPRequests2()
479479
{
480480
_host.BuildParameters.MaxNodeCount = 3;
@@ -511,7 +511,7 @@ public void SchedulerShouldHonorDisableInprocNode()
511511
/// Make sure that traversal projects are marked with an affinity of "InProc", which means that
512512
/// even if multiple are available, we should still only have the single inproc node.
513513
/// </summary>
514-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
514+
[Fact]
515515
public void TestTraversalAffinityIsInProc()
516516
{
517517
_host.BuildParameters.MaxNodeCount = 3;
@@ -560,7 +560,7 @@ public void TestProxyAffinityIsInProc()
560560
/// With something approximating the BuildManager's build loop, make sure that we don't end up
561561
/// trying to create more nodes than we can actually support.
562562
/// </summary>
563-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
563+
[Fact]
564564
public void VerifyNoOverCreationOfNodesWithBuildLoop()
565565
{
566566
// Since we're creating our own BuildManager, we need to make sure that the default
@@ -615,7 +615,7 @@ public void BuildResultNotPlacedInCurrentCacheIfConfigExistsInOverrideCache()
615615
/// <summary>
616616
/// Verify that if we get two requests but one of them is a failure, we only get the failure result back.
617617
/// </summary>
618-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
618+
[Fact]
619619
public void TestTwoRequestsWithFirstFailure()
620620
{
621621
CreateConfiguration(1, "foo.proj");
@@ -634,7 +634,7 @@ public void TestTwoRequestsWithFirstFailure()
634634
/// <summary>
635635
/// Verify that if we get two requests but one of them is a failure, we only get the failure result back.
636636
/// </summary>
637-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
637+
[Fact]
638638
public void TestTwoRequestsWithSecondFailure()
639639
{
640640
CreateConfiguration(1, "foo.proj");
@@ -653,7 +653,7 @@ public void TestTwoRequestsWithSecondFailure()
653653
/// <summary>
654654
/// Verify that if we get three requests but one of them is a failure, we only get the failure result back.
655655
/// </summary>
656-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
656+
[Fact]
657657
public void TestThreeRequestsWithOneFailure()
658658
{
659659
CreateConfiguration(1, "foo.proj");
@@ -673,7 +673,7 @@ public void TestThreeRequestsWithOneFailure()
673673
/// <summary>
674674
/// Verify that providing a result to the only outstanding request results in build complete.
675675
/// </summary>
676-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
676+
[Fact]
677677
public void TestResult()
678678
{
679679
CreateConfiguration(1, "foo.proj");
@@ -697,7 +697,7 @@ public void TestResult()
697697
/// <summary>
698698
/// Tests that the detailed summary setting causes the summary to be produced.
699699
/// </summary>
700-
[Fact(Skip = "https://github.com/dotnet/msbuild/issues/515")]
700+
[Fact]
701701
public void TestDetailedSummary()
702702
{
703703
string contents = ObjectModelHelpers.CleanupFileContents(@"

src/Build/BackEnd/Components/Scheduler/Scheduler.cs

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,49 +2239,55 @@ private bool RequestOrAnyItIsBlockedByCanBeServiced(SchedulableRequest request)
22392239
}
22402240

22412241
/// <summary>
2242-
/// Determines if we have a matching request somewhere, and if so, assigns the same request ID. Otherwise
2243-
/// assigns a new request id.
2242+
/// Determines if we have a matching request somewhere, and if so, assigns the same request ID.
2243+
/// Otherwise assigns a new request id.
22442244
/// </summary>
2245-
/// <remarks>
2246-
/// UNDONE: (Performance) This algorithm should be modified so we don't have to iterate over all of the
2247-
/// requests to find a matching one. A HashSet with proper equality semantics and a good hash code for the BuildRequest
2248-
/// would speed this considerably, especially for large numbers of projects in a build.
2249-
/// </remarks>
22502245
/// <param name="request">The request whose ID should be assigned</param>
22512246
private void AssignGlobalRequestId(BuildRequest request)
22522247
{
2253-
bool assignNewId = false;
2254-
if (request.GlobalRequestId == BuildRequest.InvalidGlobalRequestId && _schedulingData.GetRequestsAssignedToConfigurationCount(request.ConfigurationId) > 0)
2248+
// Quick exit if already assigned or if there are no requests for this configuration
2249+
if (request.GlobalRequestId != BuildRequest.InvalidGlobalRequestId
2250+
|| _schedulingData.GetRequestsAssignedToConfigurationCount(request.ConfigurationId) == 0)
2251+
{
2252+
request.GlobalRequestId = _nextGlobalRequestId++;
2253+
return;
2254+
}
2255+
2256+
HashSet<string> requestTargetsSet = new(request.Targets, StringComparer.OrdinalIgnoreCase);
2257+
2258+
// Look for matching requests in the configuration
2259+
foreach (SchedulableRequest existingRequest in _schedulingData.GetRequestsAssignedToConfiguration(request.ConfigurationId))
22552260
{
2256-
foreach (SchedulableRequest existingRequest in _schedulingData.GetRequestsAssignedToConfiguration(request.ConfigurationId))
2261+
if (TargetsMatch(requestTargetsSet, existingRequest.BuildRequest.Targets))
22572262
{
2258-
if (existingRequest.BuildRequest.Targets.Count == request.Targets.Count)
2259-
{
2260-
List<string> leftTargets = new List<string>(existingRequest.BuildRequest.Targets);
2261-
List<string> rightTargets = new List<string>(request.Targets);
2263+
request.GlobalRequestId = existingRequest.BuildRequest.GlobalRequestId;
2264+
return;
2265+
}
2266+
}
22622267

2263-
leftTargets.Sort(StringComparer.OrdinalIgnoreCase);
2264-
rightTargets.Sort(StringComparer.OrdinalIgnoreCase);
2265-
for (int i = 0; i < leftTargets.Count; i++)
2266-
{
2267-
if (!leftTargets[i].Equals(rightTargets[i], StringComparison.OrdinalIgnoreCase))
2268-
{
2269-
assignNewId = true;
2270-
break;
2271-
}
2272-
}
2268+
// No matching request found, assign a new ID
2269+
request.GlobalRequestId = _nextGlobalRequestId++;
2270+
}
22732271

2274-
if (!assignNewId)
2275-
{
2276-
request.GlobalRequestId = existingRequest.BuildRequest.GlobalRequestId;
2277-
return;
2278-
}
2279-
}
2272+
/// <summary>
2273+
/// Determines if two target collections contain the same targets, ignoring order and case.
2274+
/// </summary>
2275+
private bool TargetsMatch(HashSet<string> firstTargetsSet, List<string> secondTargetsList)
2276+
{
2277+
if (firstTargetsSet.Count != secondTargetsList.Count)
2278+
{
2279+
return false;
2280+
}
2281+
2282+
foreach (string target in secondTargetsList)
2283+
{
2284+
if (!firstTargetsSet.Contains(target))
2285+
{
2286+
return false;
22802287
}
22812288
}
22822289

2283-
request.GlobalRequestId = _nextGlobalRequestId;
2284-
_nextGlobalRequestId++;
2290+
return true;
22852291
}
22862292

22872293
/// <summary>

0 commit comments

Comments
 (0)