Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.15
3.3.16
82 changes: 45 additions & 37 deletions src/VirtualClient/VirtualClient.Core/ProfileTiming.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,48 +160,45 @@ public static ProfileTiming OneIteration()
/// </summary>
public Task MonitorTimeoutAsync(ProfileExecutor profileExecutor, CancellationToken cancellationToken)
{
// Subscribe to the relevant profile execution events SYNCHRONOUSLY here (before the background
// monitoring task below is started). The profile action loop can execute very quickly (e.g. when
// a minimal execution interval is defined). Deferring the event subscription into the background
// Task.Run can miss notifications from the very first iteration(s)/action(s) and cause an extra
// iteration/action to run beyond the number requested.
//
// We support 4 distinct scenarios for defining a timeout on the command line.
// 1) No timeout. Application runs until explicitly stopped. This is indicated by
// not supplying a --timeout on the command line.
//
// 2) Explicit timeout. The application will timeout at the time defined.
//
// 3) Explicit timeout/deterministic. The application will timeout at the time defined but ONLY after
// the current profile action has completed.
//
// 4) Explicit timeout/deterministic/*. The application will timeout at the time defined but ONLY after
// the current round of all profile actions have completed.
bool trackIterations = this.ProfileIterations != null || this.LevelOfDeterminism == DeterminismScope.AllActions;
bool trackActions = this.LevelOfDeterminism == DeterminismScope.IndividualAction;

if (trackIterations)
{
// Timeout when the expected number of profile iterations finishes or when
// an explicit timeout is hit and all profile actions have completed.
profileExecutor.IterationEnd += this.OnIterationEnd;
}
else if (trackActions)
{
// Timeout when an explicit timeout is hit and the current action is completed.
profileExecutor.ActionEnd += this.OnActionEnd;
}

return Task.Run(async () =>
{
try
{
// We support 4 distinct scenarios for defining a timeout on the command line.
// 1) No timeout. Application runs until explicitly stopped. This is indicated by
// not supplying a --timeout on the command line.
//
// 2) Explicit timeout. The application will timeout at the time defined.
//
// 3) Explicit timeout/deterministic. The application will timeout at the time defined but ONLY after
// the current profile action has completed.
//
// 4) Explicit timeout/deterministic/*. The application will timeout at the time defined but ONLY after
// the current round of all profile actions have completed.

if (this.ProfileIterations != null || this.LevelOfDeterminism == DeterminismScope.AllActions)
{
try
{
// Timeout when the expected number of profile iterations finishes or when
// an explicit timeout is hit and all profile actions have completed.
profileExecutor.IterationEnd += this.OnIterationEnd;
await this.WaitForTimeoutAsync(cancellationToken).ConfigureAwait(false);
}
finally
{
profileExecutor.IterationEnd -= this.OnIterationEnd;
}
}
else if (this.LevelOfDeterminism == DeterminismScope.IndividualAction)
if (trackIterations || trackActions)
{
try
{
// Timeout when an explicit timeout is hit and the current action is completed.
profileExecutor.ActionEnd += this.OnActionEnd;
await this.WaitForTimeoutAsync(cancellationToken).ConfigureAwait(false);
}
finally
{
profileExecutor.IterationEnd -= this.OnActionEnd;
}
await this.WaitForTimeoutAsync(cancellationToken).ConfigureAwait(false);
}
else if (this.Timeout != null)
{
Expand All @@ -219,6 +216,17 @@ public Task MonitorTimeoutAsync(ProfileExecutor profileExecutor, CancellationTok
{
// Expected sometimes when the application is explicitly cancelled (e.g. Ctrl-C).
}
finally
{
if (trackIterations)
{
profileExecutor.IterationEnd -= this.OnIterationEnd;
}
else if (trackActions)
{
profileExecutor.ActionEnd -= this.OnActionEnd;
}
}
});
}

Expand Down
Loading