From d2990170d021f2caee2c6e66368f07792c7ac53c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 Jan 2024 13:53:31 +0900 Subject: [PATCH] Add retry loop to avoid log export failing occasionally on windows Closes https://github.com/ppy/osu/issues/26693. --- osu.Game/IO/MigratableStorage.cs | 40 ++--------- .../Sections/General/UpdateSettings.cs | 4 +- osu.Game/Utils/FileUtils.cs | 72 +++++++++++++++++++ 3 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 osu.Game/Utils/FileUtils.cs diff --git a/osu.Game/IO/MigratableStorage.cs b/osu.Game/IO/MigratableStorage.cs index 14a3c5a43c..d03d259f71 100644 --- a/osu.Game/IO/MigratableStorage.cs +++ b/osu.Game/IO/MigratableStorage.cs @@ -6,8 +6,8 @@ using System; using System.IO; using System.Linq; -using System.Threading; using osu.Framework.Platform; +using osu.Game.Utils; namespace osu.Game.IO { @@ -81,7 +81,7 @@ namespace osu.Game.IO if (IgnoreSuffixes.Any(suffix => fi.Name.EndsWith(suffix, StringComparison.Ordinal))) continue; - allFilesDeleted &= AttemptOperation(() => fi.Delete(), throwOnFailure: false); + allFilesDeleted &= FileUtils.AttemptOperation(() => fi.Delete(), throwOnFailure: false); } foreach (DirectoryInfo dir in target.GetDirectories()) @@ -92,11 +92,11 @@ namespace osu.Game.IO if (IgnoreSuffixes.Any(suffix => dir.Name.EndsWith(suffix, StringComparison.Ordinal))) continue; - allFilesDeleted &= AttemptOperation(() => dir.Delete(true), throwOnFailure: false); + allFilesDeleted &= FileUtils.AttemptOperation(() => dir.Delete(true), throwOnFailure: false); } if (target.GetFiles().Length == 0 && target.GetDirectories().Length == 0) - allFilesDeleted &= AttemptOperation(target.Delete, throwOnFailure: false); + allFilesDeleted &= FileUtils.AttemptOperation(target.Delete, throwOnFailure: false); return allFilesDeleted; } @@ -115,7 +115,7 @@ namespace osu.Game.IO if (IgnoreSuffixes.Any(suffix => fileInfo.Name.EndsWith(suffix, StringComparison.Ordinal))) continue; - AttemptOperation(() => + FileUtils.AttemptOperation(() => { fileInfo.Refresh(); @@ -139,35 +139,5 @@ namespace osu.Game.IO CopyRecursive(dir, destination.CreateSubdirectory(dir.Name), false); } } - - /// - /// Attempt an IO operation multiple times and only throw if none of the attempts succeed. - /// - /// The action to perform. - /// The number of attempts (250ms wait between each). - /// Whether to throw an exception on failure. If false, will silently fail. - protected static bool AttemptOperation(Action action, int attempts = 10, bool throwOnFailure = true) - { - while (true) - { - try - { - action(); - return true; - } - catch (Exception) - { - if (attempts-- == 0) - { - if (throwOnFailure) - throw; - - return false; - } - } - - Thread.Sleep(250); - } - } } } diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 3ff5556f4d..fe88413e6a 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -15,6 +15,7 @@ using osu.Game.Localisation; using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Updater; +using osu.Game.Utils; using SharpCompress.Archives.Zip; namespace osu.Game.Overlays.Settings.Sections.General @@ -111,7 +112,8 @@ namespace osu.Game.Overlays.Settings.Sections.General using (var outStream = storage.CreateFileSafely(archive_filename)) using (var zip = ZipArchive.Create()) { - foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) zip.AddEntry(f, logStorage.GetStream(f), true); + foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) + FileUtils.AttemptOperation(z => z.AddEntry(f, logStorage.GetStream(f), true), zip); zip.SaveTo(outStream); } diff --git a/osu.Game/Utils/FileUtils.cs b/osu.Game/Utils/FileUtils.cs new file mode 100644 index 0000000000..063ab178f7 --- /dev/null +++ b/osu.Game/Utils/FileUtils.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading; + +namespace osu.Game.Utils +{ + public static class FileUtils + { + /// + /// Attempt an IO operation multiple times and only throw if none of the attempts succeed. + /// + /// The action to perform. + /// The provided state. + /// The number of attempts (250ms wait between each). + /// Whether to throw an exception on failure. If false, will silently fail. + public static bool AttemptOperation(Action action, T state, int attempts = 10, bool throwOnFailure = true) + { + while (true) + { + try + { + action(state); + return true; + } + catch (Exception) + { + if (attempts-- == 0) + { + if (throwOnFailure) + throw; + + return false; + } + } + + Thread.Sleep(250); + } + } + + /// + /// Attempt an IO operation multiple times and only throw if none of the attempts succeed. + /// + /// The action to perform. + /// The number of attempts (250ms wait between each). + /// Whether to throw an exception on failure. If false, will silently fail. + public static bool AttemptOperation(Action action, int attempts = 10, bool throwOnFailure = true) + { + while (true) + { + try + { + action(); + return true; + } + catch (Exception) + { + if (attempts-- == 0) + { + if (throwOnFailure) + throw; + + return false; + } + } + + Thread.Sleep(250); + } + } + } +}