Fix cancellation of downloads.

The Download class cancels asynchronously, which means callers must handle cancellation inside the completion event, and not after requesting cancellation.
This commit is contained in:
RoosterDragon
2016-07-30 16:27:45 +01:00
parent 77d0a8c54e
commit 3b5798b5e8
6 changed files with 61 additions and 55 deletions

View File

@@ -17,8 +17,8 @@ namespace OpenRA
{
public class Download
{
readonly object syncObject = new object();
WebClient wc;
bool cancelled;
public static string FormatErrorMessage(Exception e)
{
@@ -28,6 +28,8 @@ namespace OpenRA
switch (ex.Status)
{
case WebExceptionStatus.RequestCanceled:
return "Cancelled";
case WebExceptionStatus.NameResolutionFailure:
return "DNS lookup failed";
case WebExceptionStatus.Timeout:
@@ -41,40 +43,42 @@ namespace OpenRA
}
}
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs, bool> onComplete)
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs> onComplete)
{
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += Cancel;
wc.DownloadFileCompleted += (_, a) => { Game.OnQuit -= Cancel; };
wc.DownloadFileAsync(new Uri(url), path);
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadFileAsync(new Uri(url), path);
}
}
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs, bool> onComplete)
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs> onComplete)
{
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += Cancel;
wc.DownloadDataCompleted += (_, a) => { Game.OnQuit -= Cancel; };
wc.DownloadDataAsync(new Uri(url));
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadDataAsync(new Uri(url));
}
}
public void Cancel()
void DisposeWebClient()
{
Game.OnQuit -= Cancel;
wc.CancelAsync();
wc.Dispose();
cancelled = true;
lock (syncObject)
{
wc.Dispose();
wc = null;
}
}
public void CancelAsync()
{
lock (syncObject)
if (wc != null)
wc.CancelAsync();
}
}
}

View File

@@ -128,11 +128,11 @@ namespace OpenRA
var url = Game.Settings.Game.MapRepository + "hash/" + string.Join(",", maps.Keys) + "/yaml";
Action<DownloadDataCompletedEventArgs, bool> onInfoComplete = (i, cancelled) =>
Action<DownloadDataCompletedEventArgs> onInfoComplete = i =>
{
if (cancelled || i.Error != null)
if (i.Error != null)
{
Log.Write("debug", "Remote map query failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
Log.Write("debug", "Remote map query failed with error: {0}", Download.FormatErrorMessage(i.Error));
Log.Write("debug", "URL was: {0}", url);
foreach (var p in maps.Values)
p.UpdateRemoteSearch(MapStatus.Unavailable, null);

View File

@@ -444,13 +444,13 @@ namespace OpenRA
}
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i => { DownloadBytes = i.BytesReceived; DownloadPercentage = i.ProgressPercentage; };
Action<DownloadDataCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
Action<DownloadDataCompletedEventArgs> onDownloadComplete = i =>
{
download = null;
if (cancelled || i.Error != null)
if (i.Error != null)
{
Log.Write("debug", "Remote map download failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
Log.Write("debug", "Remote map download failed with error: {0}", Download.FormatErrorMessage(i.Error));
Log.Write("debug", "URL was: {0}", mapUrl);
innerData.Status = MapStatus.DownloadError;
@@ -487,7 +487,7 @@ namespace OpenRA
if (download == null)
return;
download.Cancel();
download.CancelAsync();
download = null;
}