Skip to content

Commit 52b57f0

Browse files
authored
Backport recent fixes (#4632)
Co-authored-by: Amaury Levé <[email protected]> Fix timing issue in parallel execution (#4629)
1 parent f6bfa29 commit 52b57f0

File tree

11 files changed

+154
-67
lines changed

11 files changed

+154
-67
lines changed

eng/Versions.props

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
is trying to parse that version and will consider any version with more than 4 `.` in it as invalid.
99
-->
1010
<SemanticVersioningV1>true</SemanticVersioningV1>
11-
<VersionPrefix>17.7.0</VersionPrefix>
11+
<VersionPrefix>17.7.1</VersionPrefix>
1212
<PreReleaseVersionLabel>release</PreReleaseVersionLabel>
1313
</PropertyGroup>
1414
<PropertyGroup Label="Arcade settings">
@@ -36,7 +36,6 @@
3636
<MicrosoftVisualStudioEnterpriseAspNetHelper>$(MicrosoftVisualStudioDiagnosticsUtilitiesVersion)</MicrosoftVisualStudioEnterpriseAspNetHelper>
3737
<MicrosoftVisualStudioInteropVersion>17.3.32622.426</MicrosoftVisualStudioInteropVersion>
3838
<MicrosoftVSSDKBuildToolsVersion>17.4.2116</MicrosoftVSSDKBuildToolsVersion>
39-
<NETStandardLibraryVersion>2.0.3</NETStandardLibraryVersion>
4039
<NewtonsoftJsonVersion>13.0.1</NewtonsoftJsonVersion>
4140
<RoslynBannedApiAnalyzersVersion>3.3.3</RoslynBannedApiAnalyzersVersion>
4241
<RoslynPublicApiAnalyzersVersion>3.3.4-beta1.21554.2</RoslynPublicApiAnalyzersVersion>

eng/verify-nupkgs.ps1

+8-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function Verify-Nuget-Packages {
1818
"Microsoft.NET.Test.Sdk" = 16;
1919
"Microsoft.TestPlatform" = 608;
2020
"Microsoft.TestPlatform.Build" = 21;
21-
"Microsoft.TestPlatform.CLI" = 493;
21+
"Microsoft.TestPlatform.CLI" = 472;
2222
"Microsoft.TestPlatform.Extensions.TrxLogger" = 35;
2323
"Microsoft.TestPlatform.ObjectModel" = 93;
2424
"Microsoft.TestPlatform.AdapterUtilities" = 34;
@@ -34,26 +34,26 @@ function Verify-Nuget-Packages {
3434
$pattern = "*.$versionPrefix*.nupkg"
3535
$nugetPackages = @(Get-ChildItem $packageDirectory -Filter $pattern -Recurse -File | Where-Object { $_.Name -notLike "*.symbols.nupkg"})
3636

37-
if (0 -eq $nugetPackages.Length) {
37+
if (0 -eq $nugetPackages.Length) {
3838
throw "No nuget packages matching $pattern were found in '$packageDirectory'."
3939
}
4040

4141
$suffixes = @($nugetPackages -replace ".*?$([regex]::Escape($versionPrefix))(.*)\.nupkg", '$1' | Sort-Object -Unique)
42-
if (1 -lt $suffixes.Length) {
42+
if (1 -lt $suffixes.Length) {
4343
Write-Host "There are two different suffixes matching the same version prefix: '$($suffixes -join "', '")'".
4444

45-
$latestNuget = $nugetPackages |
46-
Where-Object { $_.Name -like "Microsoft.TestPlatform.ObjectModel.*" } |
47-
Sort-Object -Property LastWriteTime -Descending |
45+
$latestNuget = $nugetPackages |
46+
Where-Object { $_.Name -like "Microsoft.TestPlatform.ObjectModel.*" } |
47+
Sort-Object -Property LastWriteTime -Descending |
4848
Select-Object -First 1
49-
49+
5050
$suffix = $suffixes | Where { $latestNuget.Name.Contains("$versionPrefix$_.nupkg") }
5151
$version = "$versionPrefix$suffix"
5252
Write-Host "The most recently written Microsoft.TestPlatform.ObjectModel.* nuget, is $($latestNuget.Name), which has '$suffix' suffix. Selecting only packages with that suffix."
5353

5454
$nugetPackages = $nugetPackages | Where-Object { $_.Name -like "*$version.nupkg" }
5555
}
56-
else {
56+
else {
5757
$suffix = $suffixes[0]
5858
$version = "$versionPrefix$suffix"
5959
}

src/DataCollectors/DumpMinitool.arm64/DumpMinitool.arm64.csproj

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
<RuntimeIdentifier Condition=" '$(DotNetBuildFromSource)' != 'true' ">win10-arm64</RuntimeIdentifier>
1111
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
1212
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
13+
<!--
14+
NETSDK1201: For projects targeting .NET 8.0 and higher, specifying a RuntimeIdentifier will no longer produce a
15+
self contained app by default. To continue building self-contained apps, set the SelfContained property to true
16+
or use the -\-self-contained argument.
17+
-->
18+
<MSBuildWarningsAsMessages>NETSDK1201</MSBuildWarningsAsMessages>
19+
<NoWarn>$(NoWarn);NETSDK1201</NoWarn>
1320
</PropertyGroup>
1421

1522
<ItemGroup>

src/DataCollectors/DumpMinitool.x86/DumpMinitool.x86.csproj

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
<RuntimeIdentifier Condition=" '$(DotNetBuildFromSource)' != 'true' ">win7-x86</RuntimeIdentifier>
1111
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
1212
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
13+
<!--
14+
NETSDK1201: For projects targeting .NET 8.0 and higher, specifying a RuntimeIdentifier will no longer produce a
15+
self contained app by default. To continue building self-contained apps, set the SelfContained property to true
16+
or use the -\-self-contained argument.
17+
-->
18+
<MSBuildWarningsAsMessages>NETSDK1201</MSBuildWarningsAsMessages>
19+
<NoWarn>$(NoWarn);NETSDK1201</NoWarn>
1320
</PropertyGroup>
1421

1522
<ItemGroup>

src/DataCollectors/DumpMinitool/DumpMinitool.csproj

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
<RuntimeIdentifier Condition=" '$(DotNetBuildFromSource)' != 'true' ">win7-x64</RuntimeIdentifier>
1111
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
1212
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
13+
<!--
14+
NETSDK1201: For projects targeting .NET 8.0 and higher, specifying a RuntimeIdentifier will no longer produce a
15+
self contained app by default. To continue building self-contained apps, set the SelfContained property to true
16+
or use the -\-self-contained argument.
17+
-->
18+
<MSBuildWarningsAsMessages>NETSDK1201</MSBuildWarningsAsMessages>
19+
<NoWarn>$(NoWarn);NETSDK1201</NoWarn>
1320
</PropertyGroup>
1421

1522
<ItemGroup>

src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelOperationManager.cs

+51-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel;
1111
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
12+
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
1213

1314
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client;
1415

@@ -63,6 +64,8 @@ public ParallelOperationManager(Func<TestRuntimeProviderInfo, TWorkload, TManage
6364

6465
private void ClearSlots(bool acceptMoreWork)
6566
{
67+
EqtTrace.Verbose($"ParallelOperationManager.ClearSlots: Clearing all slots. Slots should accept more work: {acceptMoreWork}");
68+
6669
lock (_lock)
6770
{
6871
_acceptMoreWork = acceptMoreWork;
@@ -76,6 +79,13 @@ private void SetOccupiedSlotCount()
7679
{
7780
AvailableSlotCount = _managerSlots.Count(s => !s.HasWork);
7881
OccupiedSlotCount = _managerSlots.Count - AvailableSlotCount;
82+
83+
if (EqtTrace.IsVerboseEnabled)
84+
{
85+
EqtTrace.Verbose($"ParallelOperationManager.SetOccupiedSlotCount: Setting slot counts AvailableSlotCount = {AvailableSlotCount}, OccupiedSlotCount = {OccupiedSlotCount}.");
86+
EqtTrace.Verbose($"Occupied slots:\n{(string.Join("\n", _managerSlots.Where(s => s.HasWork).Select((slot) => $"{slot.Index}: {GetSourcesForSlotExpensive(slot)}").ToArray()))}");
87+
88+
}
7989
}
8090

8191
public void StartWork(
@@ -91,6 +101,7 @@ public void StartWork(
91101
_initializeWorkload = initializeWorkload ?? throw new ArgumentNullException(nameof(initializeWorkload));
92102
_runWorkload = runWorkload ?? throw new ArgumentNullException(nameof(runWorkload));
93103

104+
EqtTrace.Verbose($"ParallelOperationManager.StartWork: Starting adding {workloads.Count} workloads.");
94105
_workloads.AddRange(workloads);
95106

96107
ClearSlots(acceptMoreWork: true);
@@ -123,7 +134,10 @@ private bool RunWorkInParallel()
123134
// so when it is allowed to enter it will try to add more work, but we already cancelled,
124135
// so we should not start more work.
125136
if (!_acceptMoreWork)
137+
{
138+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: We don't accept more work, returning false.");
126139
return false;
140+
}
127141

128142
// We grab all empty slots.
129143
var availableSlots = _managerSlots.Where(slot => !slot.HasWork).ToImmutableArray();
@@ -136,11 +150,10 @@ private bool RunWorkInParallel()
136150
var workloadsToAdd = availableWorkloads.Take(amount).ToImmutableArray();
137151

138152
// We associate each workload to a slot, if we reached the max parallel
139-
// level, then we will run only initalize step of the given workload.
153+
// level, then we will run only initialize step of the given workload.
140154
for (int i = 0; i < amount; i++)
141155
{
142156
var slot = availableSlots[i];
143-
slot.HasWork = true;
144157
var workload = workloadsToAdd[i];
145158
slot.ShouldPreStart = occupiedSlots + i + 1 > MaxParallelLevel;
146159

@@ -152,6 +165,13 @@ private bool RunWorkInParallel()
152165
slot.Work = workload.Work;
153166

154167
_workloads.Remove(workload);
168+
169+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: Adding 1 workload to slot, remaining workloads {_workloads.Count}.");
170+
171+
// This must be set last, every loop below looks at this property,
172+
// and they can do so from a different thread. So if we mark it as HasWork before actually assigning the work
173+
// we can pick up the slot, but it has no associated work yet.
174+
slot.HasWork = true;
155175
}
156176

157177
slots = _managerSlots.ToArray();
@@ -172,12 +192,16 @@ private bool RunWorkInParallel()
172192
{
173193
startedWork++;
174194
slot.IsRunning = true;
175-
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: Running on pre-started host: {(DateTime.Now.TimeOfDay - slot.PreStartTime).TotalMilliseconds}ms {slot.InitTask?.Status}");
195+
if (EqtTrace.IsVerboseEnabled)
196+
{
197+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: Running on pre-started host for work (source) {GetSourcesForSlotExpensive(slot)}: {(DateTime.Now.TimeOfDay - slot.PreStartTime).TotalMilliseconds}ms {slot.InitTask?.Status}");
198+
}
176199
_runWorkload(slot.Manager!, slot.EventHandler!, slot.Work!, slot.IsPreStarted, slot.InitTask);
177200

178201
// We already started as many as we were allowed, jump out;
179202
if (startedWork == MaxParallelLevel)
180203
{
204+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: We started {startedWork} work items, which is the max parallel level. Won't start more work.");
181205
break;
182206
}
183207
}
@@ -194,14 +218,18 @@ private bool RunWorkInParallel()
194218
{
195219
startedWork++;
196220
slot.IsRunning = true;
197-
EqtTrace.Verbose("ParallelOperationManager.RunWorkInParallel: Started work on a host.");
221+
if (EqtTrace.IsVerboseEnabled)
222+
{
223+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: Started host in slot number {slot.Index} for work (source): {GetSourcesForSlotExpensive(slot)}.");
224+
}
198225
_runWorkload(slot.Manager!, slot.EventHandler!, slot.Work!, slot.IsPreStarted, slot.InitTask);
199226
}
200227
}
201228

202229
// We already started as many as we were allowed, jump out;
203230
if (startedWork == MaxParallelLevel)
204231
{
232+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: We started {startedWork} work items, which is the max parallel level. Won't start more work.");
205233
break;
206234
}
207235
}
@@ -215,14 +243,19 @@ private bool RunWorkInParallel()
215243
preStartedWork++;
216244
slot.PreStartTime = DateTime.Now.TimeOfDay;
217245
slot.IsPreStarted = true;
218-
EqtTrace.Verbose("ParallelOperationManager.RunWorkInParallel: Pre-starting a host.");
246+
if (EqtTrace.IsVerboseEnabled)
247+
{
248+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: Pre-starting a host for work (source): {GetSourcesForSlotExpensive(slot)}.");
249+
}
219250
slot.InitTask = _initializeWorkload!(slot.Manager!, slot.EventHandler!, slot.Work!);
220251
}
221252
}
222253

223254
// Return true when we started more work. Or false, when there was nothing more to do.
224255
// This will propagate to handling of partial discovery or partial run.
225-
return preStartedWork + startedWork > 0;
256+
var weAddedMoreWork = preStartedWork + startedWork > 0;
257+
EqtTrace.Verbose($"ParallelOperationManager.RunWorkInParallel: We started {preStartedWork + startedWork} work items in here, returning {weAddedMoreWork}.");
258+
return weAddedMoreWork;
226259
}
227260

228261
public bool RunNextWork(TManager completedManager)
@@ -258,6 +291,10 @@ private void ClearCompletedSlot(TManager completedManager)
258291
throw new InvalidOperationException("The provided manager was found in multiple slots.");
259292
}
260293

294+
if (EqtTrace.IsVerboseEnabled)
295+
{
296+
EqtTrace.Verbose($"ParallelOperationManager.ClearCompletedSlot: Clearing slot number {completedSlot[0].Index} with work (source): {GetSourcesForSlotExpensive(completedSlot[0])}.");
297+
}
261298
var slot = completedSlot[0];
262299
slot.PreStartTime = TimeSpan.Zero;
263300
slot.Work = default(TWorkload);
@@ -273,8 +310,14 @@ private void ClearCompletedSlot(TManager completedManager)
273310
}
274311
}
275312

313+
private static string GetSourcesForSlotExpensive(ParallelOperationManager<TManager, TEventHandler, TWorkload>.Slot slot)
314+
{
315+
return string.Join(", ", (slot.Work as DiscoveryCriteria)?.Sources ?? (slot.Work as TestRunCriteria)?.Sources ?? Array.Empty<string>());
316+
}
317+
276318
public void DoActionOnAllManagers(Action<TManager> action, bool doActionsInParallel = false)
277319
{
320+
EqtTrace.Verbose($"ParallelOperationManager.DoActionOnAllManagers: Calling an action on all managers.");
278321
// We don't need to lock here, we just grab the current list of
279322
// slots that are occupied (have managers) and run action on each one of them.
280323
var managers = _managerSlots.Where(slot => slot.HasWork).Select(slot => slot.Manager).ToImmutableArray();
@@ -320,11 +363,13 @@ private static void DoManagerAction(Action action)
320363

321364
internal void StopAllManagers()
322365
{
366+
EqtTrace.Verbose($"ParallelOperationManager.StopAllManagers: Stopping all managers.");
323367
ClearSlots(acceptMoreWork: false);
324368
}
325369

326370
public void Dispose()
327371
{
372+
EqtTrace.Verbose($"ParallelOperationManager.Dispose: Disposing all managers.");
328373
ClearSlots(acceptMoreWork: false);
329374
}
330375

src/Microsoft.TestPlatform.CrossPlatEngine/Client/Parallel/ParallelProxyDiscoveryManager.cs

+6
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ private void DiscoverTestsOnConcurrentManager(
287287
bool initialized,
288288
Task? task)
289289
{
290+
// If we do the scheduling incorrectly this will get null. It should not happen, but it has happened before.
291+
if (discoveryCriteria == null)
292+
{
293+
throw new ArgumentNullException(nameof(discoveryCriteria));
294+
}
295+
290296
// Kick off another discovery task for the next source
291297
Task.Run(() =>
292298
{

0 commit comments

Comments
 (0)