Skip to content

Commit e04e2d7

Browse files
authored
Fix IOException when Console window unavailable (#2237) (#2238)
* Fix IOException when Console window unavailable (#2237) * Refactored ConsoleTitler (#2237)
1 parent dc7734d commit e04e2d7

File tree

2 files changed

+86
-24
lines changed

2 files changed

+86
-24
lines changed

src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
using BenchmarkDotNet.Jobs;
1818
using BenchmarkDotNet.Loggers;
1919
using BenchmarkDotNet.Mathematics;
20-
using BenchmarkDotNet.Portability;
2120
using BenchmarkDotNet.Reports;
2221
using BenchmarkDotNet.Toolchains;
2322
using BenchmarkDotNet.Toolchains.Parameters;
@@ -168,15 +167,13 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
168167
var cultureInfo = config.CultureInfo ?? DefaultCultureInfo.Instance;
169168
var reports = new List<BenchmarkReport>();
170169
string title = GetTitle(new[] { benchmarkRunInfo });
171-
var consoleTitle = RuntimeInformation.IsWindows() ? Console.Title : string.Empty;
170+
using var consoleTitler = new ConsoleTitler($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining");
172171

173172
logger.WriteLineInfo($"// Found {benchmarks.Length} benchmarks:");
174173
foreach (var benchmark in benchmarks)
175174
logger.WriteLineInfo($"// {benchmark.DisplayInfo}");
176175
logger.WriteLine();
177176

178-
UpdateTitle(totalBenchmarkCount, benchmarksToRunCount);
179-
180177
using (var powerManagementApplier = new PowerManagementApplier(logger))
181178
{
182179
bool stop = false;
@@ -241,15 +238,10 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
241238

242239
benchmarksToRunCount -= stop ? benchmarks.Length - i : 1;
243240

244-
LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, taskbarProgress);
241+
LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, consoleTitler, taskbarProgress);
245242
}
246243
}
247244

248-
if (RuntimeInformation.IsWindows())
249-
{
250-
Console.Title = consoleTitle;
251-
}
252-
253245
var runEnd = runsChronometer.GetElapsed();
254246

255247
return new Summary(title,
@@ -652,15 +644,7 @@ private static void Cleanup(HashSet<string> artifactsToCleanup)
652644
}
653645
}
654646

655-
private static void UpdateTitle(int totalBenchmarkCount, int benchmarksToRunCount)
656-
{
657-
if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
658-
{
659-
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining";
660-
}
661-
}
662-
663-
private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, TaskbarProgress taskbarProgress)
647+
private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, ConsoleTitler consoleTitler, TaskbarProgress taskbarProgress)
664648
{
665649
int executedBenchmarkCount = totalBenchmarkCount - benchmarksToRunCount;
666650
TimeSpan fromNow = GetEstimatedFinishTime(runsChronometer, benchmarksToRunCount, executedBenchmarkCount);
@@ -669,10 +653,8 @@ private static void LogProgress(ILogger logger, in StartedClock runsChronometer,
669653
$" Estimated finish {estimatedEnd:yyyy-MM-dd H:mm} ({(int)fromNow.TotalHours}h {fromNow.Minutes}m from now) **";
670654
logger.WriteLineHeader(message);
671655

672-
if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
673-
{
674-
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish";
675-
}
656+
consoleTitler.UpdateTitle ($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish");
657+
676658
taskbarProgress.SetProgress((float) executedBenchmarkCount / totalBenchmarkCount);
677659
}
678660

@@ -728,4 +710,4 @@ private static int GetIdToResume(string rootArtifactsFolderPath, string currentL
728710
return -1;
729711
}
730712
}
731-
}
713+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System;
2+
using System.IO;
3+
using BenchmarkDotNet.Portability;
4+
5+
namespace BenchmarkDotNet.Running
6+
{
7+
/// <summary>
8+
/// Updates Console.Title, subject to platform capabilities and Console availability.
9+
/// Restores the original (or fallback) title upon disposal.
10+
/// </summary>
11+
internal class ConsoleTitler : IDisposable
12+
{
13+
/// <summary>
14+
/// Whether this instance has any effect. This will be false if the platform doesn't support Console retitling,
15+
/// or if Console output is redirected.
16+
/// </summary>
17+
public bool IsEnabled { get; private set; }
18+
19+
private string oldConsoleTitle;
20+
21+
public ConsoleTitler(string initialTitle)
22+
{
23+
// Return without enabling if Console output is redirected.
24+
if (Console.IsOutputRedirected)
25+
{
26+
return;
27+
}
28+
29+
try
30+
{
31+
oldConsoleTitle = PlatformSupportsTitleRead() ? Console.Title : "";
32+
}
33+
catch (IOException)
34+
{
35+
// We're unable to read Console.Title on a platform that supports it. This can happen when no console
36+
// window is available due to the application being Windows Forms, WPF, Windows Service or a daemon.
37+
oldConsoleTitle = "";
38+
}
39+
40+
try
41+
{
42+
// Enable ConsoleTitler if and only if we can successfully set the Console.Title property.
43+
Console.Title = initialTitle;
44+
IsEnabled = true;
45+
}
46+
catch (IOException)
47+
{
48+
}
49+
catch (PlatformNotSupportedException)
50+
{
51+
// As of .NET 7, platforms other than Windows, Linux and MacOS do not support Console retitling.
52+
}
53+
}
54+
55+
#if NET6_0_OR_GREATER
56+
[System.Runtime.Versioning.SupportedOSPlatformGuard("windows")]
57+
#endif
58+
private static bool PlatformSupportsTitleRead() => RuntimeInformation.IsWindows();
59+
60+
/// <summary>
61+
/// Updates Console.Title if enabled.
62+
/// </summary>
63+
public void UpdateTitle(string title)
64+
{
65+
if (IsEnabled)
66+
{
67+
Console.Title = title;
68+
}
69+
}
70+
71+
public void Dispose()
72+
{
73+
if (IsEnabled)
74+
{
75+
Console.Title = oldConsoleTitle;
76+
IsEnabled = false;
77+
}
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)