Skip to content

Commit dde87a1

Browse files
authored
Handle OperationCanceledException from ctrl+c (#3189)
* Handle `OperationCanceledException` from ctrl+c. * Return single failed summary.
1 parent 16cafb6 commit dde87a1

2 files changed

Lines changed: 22 additions & 8 deletions

File tree

src/BenchmarkDotNet/Helpers/ExceptionHelper.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@ namespace BenchmarkDotNet.Helpers
77
public static class ExceptionHelper
88
{
99
public static bool IsProperCancelation(Exception ex, CancellationToken cancellationToken)
10-
{
11-
if (!cancellationToken.IsCancellationRequested)
12-
{
13-
return false;
14-
}
15-
return ex is OperationCanceledException ||
16-
(ex is TargetInvocationException && ex.InnerException is OperationCanceledException);
17-
}
10+
=> cancellationToken.IsCancellationRequested && IsCancelation(ex);
11+
12+
// On .NET Framework an Exception thrown from a reflection-invoked method is wrapped in a TargetInvocationException.
13+
internal static bool IsCancelation(Exception ex) =>
14+
ex is OperationCanceledException ||
15+
(ex is TargetInvocationException && ex.InnerException is OperationCanceledException);
1816

1917
public static bool IsOom(Exception ex) =>
2018
ex is OutOfMemoryException ||

src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ internal static class BenchmarkRunnerClean
3434
internal static readonly IResolver DefaultResolver = new CompositeResolver(EnvironmentResolver.Instance, InfrastructureResolver.Instance);
3535

3636
internal static async ValueTask<Summary[]> Run(BenchmarkRunInfo[] benchmarkRunInfos, CancellationToken cancellationToken)
37+
{
38+
try
39+
{
40+
return await RunCore(benchmarkRunInfos, cancellationToken).ConfigureAwait(false);
41+
}
42+
catch (Exception e) when (!cancellationToken.IsCancellationRequested && ExceptionHelper.IsCancelation(e))
43+
{
44+
// The run was cancelled via Ctrl+C (CtrlCCanceler cancels an internal linked token, so the
45+
// caller-provided token is not cancellation-requested here). CtrlCCanceler already logged the
46+
// cancellation, so we just swallow the exception instead of letting it bubble up as an unhandled
47+
// exception with a full stack trace. When the caller's own token is cancelled, we let it propagate.
48+
return [Summary.ValidationFailed("Canceled via ctrl+c", string.Empty, string.Empty)];
49+
}
50+
}
51+
52+
private static async ValueTask<Summary[]> RunCore(BenchmarkRunInfo[] benchmarkRunInfos, CancellationToken cancellationToken)
3753
{
3854
using var taskbarProgress = new TaskbarProgress(TaskbarProgressState.Indeterminate);
3955

0 commit comments

Comments
 (0)