Skip to content

Commit

Permalink
Add CancelDownload method to provider interface
Browse files Browse the repository at this point in the history
Instead of just calling Thread.Abort which is messy and doesn't let
providers clean up (for instance podcasts continue to download on a
separate thread), add a CancelDownload method to the IRadioProvider
interface which is called when a download is cancelled.

If the provider doesn't return from the main provider thread within 10
seconds, fall back to using Thread.Abort and set the download to Errored
so that the hung / blocked stack trace can be reported.

This resolves #154.
  • Loading branch information
ribbons committed Sep 24, 2014
1 parent e09bb5a commit 17dc36b
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 47 deletions.
40 changes: 32 additions & 8 deletions Classes/DownloadHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Radio Downloader.
* Copyright © 2007-2013 by the authors - see the AUTHORS file for details.
* Copyright © 2007-2014 by the authors - see the AUTHORS file for details.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
Expand Down Expand Up @@ -40,6 +40,9 @@ internal class DownloadHandler : Database
private Thread downloadThread;
private object downloadThreadLock = new object();

private bool cancelled;
private bool cancelResponse;

public DownloadHandler(int epid)
{
using (SQLiteCommand command = new SQLiteCommand("select pr.progid, pluginid, pr.image as progimg, ep.duration, ep.image as epimg, pr.extid as progextid, ep.extid as epextid from episodes as ep, programmes as pr where ep.epid=@epid and ep.progid=pr.progid", FetchDbConn()))
Expand Down Expand Up @@ -125,13 +128,34 @@ public void Start()
}
}

public void Cancel()
public bool Cancel()
{
lock (this.downloadThreadLock)
{
if (this.downloadThread != null && this.downloadThread.IsAlive)
lock (this.pluginInstanceLock)
{
this.cancelled = true;

if (this.downloadThread == null || !this.downloadThread.IsAlive)
{
return true;
}

this.pluginInstance.CancelDownload();

for (int wait = 0; wait < 20; wait++)
{
Thread.Sleep(500);

if (this.cancelResponse)
{
return true;
}
}

// No timely response to request, kill the thread
this.downloadThread.Abort();
return false;
}
}
}
Expand Down Expand Up @@ -198,11 +222,6 @@ private void DownloadProgThread()

fileExtension = this.pluginInstance.DownloadProgramme(this.progExtId, this.episodeExtId, this.providerProgInfo, this.providerEpisodeInfo, finalName);
}
catch (ThreadAbortException)
{
// The download has been cancelled
return;
}
catch (DownloadException downloadExp)
{
if (downloadExp.ErrorType == ErrorType.UnknownError)
Expand All @@ -222,6 +241,11 @@ private void DownloadProgThread()
return;
}

if (this.cancelled)
{
this.cancelResponse = true;
}

finalName += "." + fileExtension;

lock (Database.DbUpdateLock)
Expand Down
25 changes: 10 additions & 15 deletions Classes/DownloadManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Radio Downloader.
* Copyright © 2007-2012 by the authors - see the AUTHORS file for details.
* Copyright © 2007-2014 by the authors - see the AUTHORS file for details.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
Expand Down Expand Up @@ -59,27 +59,22 @@ public static void AddDownloads(int[] epids)
StartNextDownload();
}

public static void CancelDownload(int epid)
public static bool CancelDownload(int epid)
{
bool cancelled = false;

lock (downloads)
{
if (downloads.ContainsKey(epid))
if (!downloads[epid].Cancel())
{
downloads[epid].Cancel();
cancelled = true;

downloads.Remove(epid);
startedDownloads.Remove(epid);
return false;
}
}

if (cancelled)
{
UpdateTotalProgress();
StartNextDownload();
downloads.Remove(epid);
startedDownloads.Remove(epid);
}

UpdateTotalProgress();
StartNextDownload();
return true;
}

private static void ResumeDownloadsAsync()
Expand Down
5 changes: 4 additions & 1 deletion Classes/Model/Download.cs
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,10 @@ private static void RemoveAsync(int epid, bool auto)
{
if (!auto)
{
DownloadManager.CancelDownload(epid);
if (!DownloadManager.CancelDownload(epid))
{
return;
}
}

lock (Database.DbUpdateLock)
Expand Down
7 changes: 6 additions & 1 deletion Interfaces/IRadioProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Radio Downloader.
* Copyright © 2007-2012 by the authors - see the AUTHORS file for details.
* Copyright © 2007-2014 by the authors - see the AUTHORS file for details.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
Expand Down Expand Up @@ -89,6 +89,11 @@ public interface IRadioProvider
/// <exception cref="DownloadException">Thrown when an expected error is encountered whilst downloading.</exception>
/// <returns>The file extension of a successful download.</returns>
string DownloadProgramme(string progExtId, string episodeExtId, ProgrammeInfo progInfo, EpisodeInfo epInfo, string finalName);

/// <summary>
/// Cancel the episode download currently in progress.
/// </summary>
void CancelDownload();
}

/// <summary>
Expand Down
46 changes: 24 additions & 22 deletions Providers/PodcastProvider/Classes/DownloadWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of the Podcast Provider for Radio Downloader.
* Copyright © 2007-2013 by the authors - see the AUTHORS file for details.
* Copyright © 2007-2014 by the authors - see the AUTHORS file for details.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
Expand All @@ -27,9 +27,6 @@ internal class DownloadWrapper

private Uri downloadUrl;
private string destPath;
private bool downloadComplete;

private Exception downloadError;

public DownloadWrapper(Uri downloadUrl, string destPath)
{
Expand All @@ -41,15 +38,11 @@ public DownloadWrapper(Uri downloadUrl, string destPath)

public event DownloadProgressEventHandler DownloadProgress;

public bool Complete
{
get { return this.downloadComplete; }
}
public bool Complete { get; private set; }

public Exception Error
{
get { return this.downloadError; }
}
public Exception Error { get; private set; }

public bool Cancelled { get; private set; }

public void Download()
{
Expand All @@ -63,6 +56,12 @@ public void Download()
this.downloadClient.DownloadFileAsync(this.downloadUrl, this.destPath);
}

public void Cancel()
{
this.Cancelled = true;
this.downloadClient.CancelAsync();
}

private void DownloadClient_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
{
if (this.DownloadProgress != null)
Expand All @@ -73,18 +72,21 @@ private void DownloadClient_DownloadProgressChanged(object sender, System.Net.Do

private void DownloadClient_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (!e.Cancelled)
if (e.Cancelled && !this.Cancelled)
{
SystemEvents.PowerModeChanged -= this.PowerModeChange;
// Cancelled before retry
return;
}

if (e.Error != null)
{
this.downloadError = e.Error;
}
else
{
this.downloadComplete = true;
}
SystemEvents.PowerModeChanged -= this.PowerModeChange;

if (e.Error != null)
{
this.Error = e.Error;
}
else if (!this.Cancelled)
{
this.Complete = true;
}
}

Expand Down
10 changes: 10 additions & 0 deletions Providers/PodcastProvider/Classes/PodcastProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ public string DownloadProgramme(string progExtId, string episodeExtId, Programme
while ((!this.doDownload.Complete) && this.doDownload.Error == null)
{
Thread.Sleep(500);

if (this.doDownload.Cancelled)
{
return null;
}
}

if (this.doDownload.Error != null)
Expand Down Expand Up @@ -433,6 +438,11 @@ public string DownloadProgramme(string progExtId, string episodeExtId, Programme
return extension;
}

public void CancelDownload()
{
this.doDownload.Cancel();
}

internal void RaiseFindNewException(Exception exception)
{
if (this.FindNewException != null)
Expand Down

0 comments on commit 17dc36b

Please sign in to comment.