Skip to content

Commit

Permalink
Fix IOException when Console window unavailable (#2237) (#2238)
Browse files Browse the repository at this point in the history
* Fix IOException when Console window unavailable (#2237)

* Refactored ConsoleTitler (#2237)
  • Loading branch information
albahari committed Jan 4, 2023
1 parent dc7734d commit e04e2d7
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 24 deletions.
30 changes: 6 additions & 24 deletions src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Mathematics;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Toolchains.Parameters;
Expand Down Expand Up @@ -168,15 +167,13 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,
var cultureInfo = config.CultureInfo ?? DefaultCultureInfo.Instance;
var reports = new List<BenchmarkReport>();
string title = GetTitle(new[] { benchmarkRunInfo });
var consoleTitle = RuntimeInformation.IsWindows() ? Console.Title : string.Empty;
using var consoleTitler = new ConsoleTitler($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining");

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

UpdateTitle(totalBenchmarkCount, benchmarksToRunCount);

using (var powerManagementApplier = new PowerManagementApplier(logger))
{
bool stop = false;
Expand Down Expand Up @@ -241,15 +238,10 @@ private static Summary Run(BenchmarkRunInfo benchmarkRunInfo,

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

LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, taskbarProgress);
LogProgress(logger, in runsChronometer, totalBenchmarkCount, benchmarksToRunCount, consoleTitler, taskbarProgress);
}
}

if (RuntimeInformation.IsWindows())
{
Console.Title = consoleTitle;
}

var runEnd = runsChronometer.GetElapsed();

return new Summary(title,
Expand Down Expand Up @@ -652,15 +644,7 @@ private static void Cleanup(HashSet<string> artifactsToCleanup)
}
}

private static void UpdateTitle(int totalBenchmarkCount, int benchmarksToRunCount)
{
if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
{
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining";
}
}

private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, TaskbarProgress taskbarProgress)
private static void LogProgress(ILogger logger, in StartedClock runsChronometer, int totalBenchmarkCount, int benchmarksToRunCount, ConsoleTitler consoleTitler, TaskbarProgress taskbarProgress)
{
int executedBenchmarkCount = totalBenchmarkCount - benchmarksToRunCount;
TimeSpan fromNow = GetEstimatedFinishTime(runsChronometer, benchmarksToRunCount, executedBenchmarkCount);
Expand All @@ -669,10 +653,8 @@ private static void LogProgress(ILogger logger, in StartedClock runsChronometer,
$" Estimated finish {estimatedEnd:yyyy-MM-dd H:mm} ({(int)fromNow.TotalHours}h {fromNow.Minutes}m from now) **";
logger.WriteLineHeader(message);

if (!Console.IsOutputRedirected && (RuntimeInformation.IsWindows() || RuntimeInformation.IsLinux() || RuntimeInformation.IsMacOS()))
{
Console.Title = $"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish";
}
consoleTitler.UpdateTitle ($"{benchmarksToRunCount}/{totalBenchmarkCount} Remaining - {(int)fromNow.TotalHours}h {fromNow.Minutes}m to finish");

taskbarProgress.SetProgress((float) executedBenchmarkCount / totalBenchmarkCount);
}

Expand Down Expand Up @@ -728,4 +710,4 @@ private static int GetIdToResume(string rootArtifactsFolderPath, string currentL
return -1;
}
}
}
}
80 changes: 80 additions & 0 deletions src/BenchmarkDotNet/Running/ConsoleTitler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using System.IO;
using BenchmarkDotNet.Portability;

namespace BenchmarkDotNet.Running
{
/// <summary>
/// Updates Console.Title, subject to platform capabilities and Console availability.
/// Restores the original (or fallback) title upon disposal.
/// </summary>
internal class ConsoleTitler : IDisposable
{
/// <summary>
/// Whether this instance has any effect. This will be false if the platform doesn't support Console retitling,
/// or if Console output is redirected.
/// </summary>
public bool IsEnabled { get; private set; }

private string oldConsoleTitle;

public ConsoleTitler(string initialTitle)
{
// Return without enabling if Console output is redirected.
if (Console.IsOutputRedirected)
{
return;
}

try
{
oldConsoleTitle = PlatformSupportsTitleRead() ? Console.Title : "";
}
catch (IOException)
{
// We're unable to read Console.Title on a platform that supports it. This can happen when no console
// window is available due to the application being Windows Forms, WPF, Windows Service or a daemon.
oldConsoleTitle = "";
}

try
{
// Enable ConsoleTitler if and only if we can successfully set the Console.Title property.
Console.Title = initialTitle;
IsEnabled = true;
}
catch (IOException)
{
}
catch (PlatformNotSupportedException)
{
// As of .NET 7, platforms other than Windows, Linux and MacOS do not support Console retitling.
}
}

#if NET6_0_OR_GREATER
[System.Runtime.Versioning.SupportedOSPlatformGuard("windows")]
#endif
private static bool PlatformSupportsTitleRead() => RuntimeInformation.IsWindows();

/// <summary>
/// Updates Console.Title if enabled.
/// </summary>
public void UpdateTitle(string title)
{
if (IsEnabled)
{
Console.Title = title;
}
}

public void Dispose()
{
if (IsEnabled)
{
Console.Title = oldConsoleTitle;
IsEnabled = false;
}
}
}
}

0 comments on commit e04e2d7

Please sign in to comment.