Skip to content

Commit f872642

Browse files
committed
Not retrying downloads upon IO errors. Applying exponential backoff + randomizer strategies to thread sleep times upon download retries.
1 parent ff9dc38 commit f872642

File tree

11 files changed

+88
-56
lines changed

11 files changed

+88
-56
lines changed

GenericAutoUpdater/Downloaders/HttpClientDownloader.cs

+20-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
using System;
1+
using GenericAutoUpdater.Hash;
2+
using GenericAutoUpdater.Resources;
3+
using GenericAutoUpdater.Resources.Configs;
4+
using GenericAutoUpdater.Resources.TextResources;
5+
using GenericAutoUpdater.UI;
6+
using System;
27
using System.ComponentModel;
38
using System.Diagnostics;
49
using System.IO;
510
using System.Net;
611
using System.Net.Http;
712
using System.Threading;
813
using System.Threading.Tasks;
9-
using GenericAutoUpdater.Hash;
10-
using GenericAutoUpdater.Resources;
11-
using GenericAutoUpdater.Resources.Configs;
12-
using GenericAutoUpdater.Resources.TextResources;
13-
using GenericAutoUpdater.UI;
1414

1515
namespace GenericAutoUpdater.Downloaders {
1616
/// <summary>
@@ -78,10 +78,11 @@ private byte[] DownloadData(string address, string expectedHash, string filePath
7878
return null;
7979
data.Wait();
8080
return data.Result;
81-
} catch (Exception ex) {
81+
}
82+
catch (Exception ex) {
8283
if (!ExceptionTypeShouldRetry(ex))
8384
throw;
84-
Thread.Sleep(DownloaderConfigs.INTERVAL_MS_BETWEEN_DOWNLOAD_RETRIES);
85+
Thread.Sleep(ComputeNextSleepTime(DownloaderConfigs.BASE_MS_SLEEP_TIME_BETWEEN_DOWNLOAD_RETRIES, tries));
8586
if (++tries == DownloaderConfigs.MAX_DOWNLOAD_RETRIES_PER_FILE)
8687
throw;
8788
continue;
@@ -195,8 +196,17 @@ private void SetupDefaultHeaders() {
195196
private bool ExceptionTypeShouldRetry(Exception ex) {
196197
// When a HTTP status code in the range of [300 - 499] is received there is no point in retrying.
197198
// When the Download's contentStream is closed by force due to a read timeout there is no point in retrying.
198-
return !(ex is AggregateException exception && (Utils.AggregateContainsSpecificException(exception, new ObjectDisposedException("")) || Utils.AggregateContainsSpecificException(exception, new HttpRequestException()))
199-
|| ex is HttpRequestException || ex is ObjectDisposedException);
199+
// There is no point in retrying IOExceptions.
200+
return !(ex is AggregateException exception && (Utils.AggregateContainsSpecificException(exception, new ObjectDisposedException("")) || Utils.AggregateContainsSpecificException(exception, new HttpRequestException()) || Utils.AggregateContainsSpecificException(exception, new IOException()))
201+
|| ex is HttpRequestException || ex is ObjectDisposedException || ex is IOException);
202+
}
203+
204+
/// <summary>
205+
/// This method calculates the next sleep time by multiplying the base sleep time (DownloaderConfigs.BASE_MS_SLEEP_TIME_BETWEEN_DOWNLOAD_RETRIES) by a random floating-point number in the interval [0, 1[.
206+
/// It then applies a exponential backoff strategy to the wait time.
207+
/// </summary>
208+
private int ComputeNextSleepTime(int baseTimer, int tries) {
209+
return Convert.ToInt32(Math.Pow(2, tries) * baseTimer * (1 + new Random().NextDouble()));
200210
}
201211
}
202212
}

GenericAutoUpdater/Engine/PatcherEngine.cs

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
using System;
2-
using System.Collections.Concurrent;
3-
using System.Collections.Generic;
4-
using System.ComponentModel;
5-
using System.Diagnostics;
6-
using System.IO;
7-
using System.Linq;
8-
using System.Text;
9-
using System.Threading;
10-
using GenericAutoUpdater.Downloaders;
1+
using GenericAutoUpdater.Downloaders;
112
using GenericAutoUpdater.ExceptionHandler;
123
using GenericAutoUpdater.ExceptionHandler.Exceptions;
134
using GenericAutoUpdater.FileSystem;
@@ -16,6 +7,15 @@
167
using GenericAutoUpdater.Resources.Configs;
178
using GenericAutoUpdater.Resources.TextResources;
189
using GenericAutoUpdater.UI;
10+
using System;
11+
using System.Collections.Concurrent;
12+
using System.Collections.Generic;
13+
using System.ComponentModel;
14+
using System.Diagnostics;
15+
using System.IO;
16+
using System.Linq;
17+
using System.Text;
18+
using System.Threading;
1919

2020
namespace GenericAutoUpdater.Engine {
2121
/// <summary>

GenericAutoUpdater/ExceptionHandler/Handler.cs

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
using System;
1+
using GenericAutoUpdater.ExceptionHandler.Exceptions;
2+
using GenericAutoUpdater.Resources.TextResources;
3+
using System;
24
using System.IO;
35
using System.Linq;
46
using System.Net;
57
using System.Net.Http;
68
using System.Security;
79
using System.Text;
810
using System.Windows.Forms;
9-
using GenericAutoUpdater.ExceptionHandler.Exceptions;
10-
using GenericAutoUpdater.Resources.TextResources;
1111

1212
namespace GenericAutoUpdater.ExceptionHandler {
1313
/// <summary>
@@ -40,21 +40,28 @@ public static void Handle(Exception ex) {
4040
case UnauthorizedAccessException e2:
4141
case PathTooLongException e3:
4242
case IOException e4:
43-
ShowError(ErrorHandlerResources.ERROR_IO_EXPLORER, ErrorHandlerResources.ERROR_TITLE_EXPLORER);
43+
ShowError(ErrorHandlerResources.ERROR_IO_EXPLORER, ErrorHandlerResources.ERROR_TITLE_EXPLORER, ex.Message);
4444
break;
4545
default:
46-
ShowError(ErrorHandlerResources.UNKNOWN_ERROR, ErrorHandlerResources.ERROR_TITLE_UNKNOWN);
46+
ShowError(ErrorHandlerResources.UNKNOWN_ERROR, ErrorHandlerResources.ERROR_TITLE_UNKNOWN, ex.Message);
4747
break;
4848
}
4949
}
5050

5151
/// <summary>
52-
/// Informs the user, through a <c>MessageBox</c> whose text and caption are received in argument, that something went wrong while patching.
52+
/// Informs the user, through a <c>MessageBox</c> (whose text and caption are received in arguments), that something went wrong while patching.
5353
/// Exits the application terminating all Threads after the user clicks in the OK button.
5454
/// </summary>
55-
private static void ShowError(string text, string caption) {
56-
MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
55+
private static void ShowError(string text, string caption, string message = "") {
56+
MessageBox.Show(BuildErrorMessage(text, message), caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
5757
Application.Exit();
5858
}
59+
60+
/// <summary>
61+
/// Builds the error message displayed to the user through the <c>ShowError</c> method.
62+
/// </summary>
63+
private static string BuildErrorMessage(string text, string message) {
64+
return text + (!message.Equals(string.Empty) ? Environment.NewLine + Environment.NewLine + message : string.Empty);
65+
}
5966
}
6067
}

GenericAutoUpdater/FileSystem/FileSystemExplorer.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using System.Collections.Concurrent;
1+
using GenericAutoUpdater.Downloaders;
2+
using GenericAutoUpdater.Hash;
3+
using System.Collections.Concurrent;
24
using System.IO;
35
using System.Threading.Tasks;
4-
using GenericAutoUpdater.Downloaders;
5-
using GenericAutoUpdater.Hash;
66

77
namespace GenericAutoUpdater.FileSystem {
88
/// <summary>

GenericAutoUpdater/Patcher.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
using System;
2-
using System.Windows.Forms;
3-
using GenericAutoUpdater.ExceptionHandler;
1+
using GenericAutoUpdater.ExceptionHandler;
42
using GenericAutoUpdater.Resources.TextResources;
3+
using System;
4+
using System.Windows.Forms;
55

66
namespace GenericAutoUpdater {
77
/// <summary>
@@ -28,12 +28,15 @@ static void Main() {
2828
Application.EnableVisualStyles();
2929
Application.SetCompatibleTextRenderingDefault(false);
3030
Application.Run(new PatcherMainWindow());
31-
} catch (Exception ex) {
31+
}
32+
catch (Exception ex) {
3233
Handler.Handle(ex);
33-
} finally {
34+
}
35+
finally {
3436
mutex.ReleaseMutex();
3537
}
36-
} else
38+
}
39+
else
3740
MessageBox.Show(MainWindowResources.ALREADY_RUNNING, MainWindowResources.ALREADY_RUNNING_ERROR, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
3841
}
3942
}

GenericAutoUpdater/Properties/AssemblyInfo.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@
3232
// by using the '*' as shown below:
3333
// [assembly: AssemblyVersion("1.0.*")]
3434
[assembly: AssemblyVersion("1.0.0.0")]
35-
[assembly: AssemblyFileVersion("1.0.1.4")]
35+
[assembly: AssemblyFileVersion("1.0.2.0")]

GenericAutoUpdater/Resources/Configs/DownloaderConfigs.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ public static class DownloaderConfigs {
66
/// <summary>
77
/// The number of attempts to successfully download a file before throwing a specific Exception.
88
/// </summary>
9-
public static readonly int MAX_DOWNLOAD_RETRIES_PER_FILE = 5;
9+
public static readonly int MAX_DOWNLOAD_RETRIES_PER_FILE = 3;
1010

1111
/// <summary>
12-
/// The time (in milliseconds) that the downloader will spend sleeping before retrying a failed download.
12+
/// The base time (in milliseconds) that the downloader will spend sleeping before retrying a failed download.
1313
/// </summary>
14-
public static readonly int INTERVAL_MS_BETWEEN_DOWNLOAD_RETRIES = 1000;
14+
public static readonly int BASE_MS_SLEEP_TIME_BETWEEN_DOWNLOAD_RETRIES = 1000;
1515

1616
/// <summary>
1717
/// The time (in milliseconds) that the downloader will spend waiting for the current read request to be completed before closing the stream by force, thus throwing an <c>ObjectDisposedException</c>.

GenericAutoUpdater/Resources/TextResources/ErrorHandlerResources.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ public static class ErrorHandlerResources {
3636
/// <summary>
3737
/// The text description of the window triggered whenever there is a consistency error.
3838
/// </summary>
39-
public static readonly string AV_FALSE_POSITIVE = "One or more files or directories are missing or were tampered after applying the patch. Please check if your AntiVirus or Microsoft Defender are at fault and then run the Auto-Updater again.";
39+
public static readonly string AV_FALSE_POSITIVE = "One or more files or directories are missing or were tampered while applying the patch. Please check if your AntiVirus or Microsoft Defender are at fault and then run the Auto-Updater again.";
4040

4141
/// <summary>
4242
/// The text description of the window triggered whenever there is an IO error.
4343
/// </summary>
44-
public static readonly string ERROR_IO_EXPLORER = "An error occurred while trying to write a file or directory in its destination path. Please check if you have permission to apply the patch in this current directory, or if this current directory is being used by another process, or if its path is too long.";
44+
public static readonly string ERROR_IO_EXPLORER = "An error occurred while trying to write a file or directory in its destination path. Please check if your AntiVirus blocked any of the downloaded files, or if you have permission to apply the patch in this current directory, or if this current directory is being used by another process, or if its path is too long.";
4545
}
4646
}

GenericAutoUpdater/Resources/Utils.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
using System;
2-
using System.ComponentModel;
3-
using GenericAutoUpdater.UI;
1+
using GenericAutoUpdater.UI;
42
using GenericAutoUpdater.UI.Wrappers;
3+
using System;
4+
using System.ComponentModel;
55

66
namespace GenericAutoUpdater.Resources {
77
/// <summary>

GenericAutoUpdater/UI/Screens/PatcherMainWindow.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using System;
2-
using System.ComponentModel;
3-
using System.Diagnostics;
4-
using System.Windows.Forms;
5-
using GenericAutoUpdater.Engine;
1+
using GenericAutoUpdater.Engine;
62
using GenericAutoUpdater.ExceptionHandler;
73
using GenericAutoUpdater.Resources.TextResources;
84
using GenericAutoUpdater.UI;
95
using GenericAutoUpdater.UI.Wrappers;
6+
using System;
7+
using System.ComponentModel;
8+
using System.Diagnostics;
9+
using System.Windows.Forms;
1010

1111
namespace GenericAutoUpdater {
1212
/// <summary>
@@ -118,7 +118,8 @@ private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWor
118118
IPatcherEngine engine = new PatcherEngine(bw);
119119
try {
120120
engine.Patch();
121-
} catch (Exception ex) {
121+
}
122+
catch (Exception ex) {
122123
Handler.Handle(ex);
123124
}
124125
}

doc/Generic Auto-Patcher.xml

+15-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)