diff --git a/src/downloader/downloader.rs b/src/downloader/downloader.rs index 8c02aab..1c934b8 100644 --- a/src/downloader/downloader.rs +++ b/src/downloader/downloader.rs @@ -30,6 +30,7 @@ use std::io::SeekFrom; use std::io::Write; use std::ops::Deref; use std::ops::DerefMut; +use std::ops::Drop; use std::path::Path; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicI64; @@ -89,6 +90,8 @@ pub struct DownloaderInternal { @@ -120,7 +123,12 @@ impl DownloaderInternal { return Ok(DownloaderResult::Canceled); } else { remove_file(p)?; - PdFile::new() + match PdFile::open(p)? { + PdFileResult::Ok(f) => f, + _ => { + panic!("This should not happened."); + } + } } } None => { @@ -171,6 +179,7 @@ impl DownloaderInternal { max_part_retry_count: AtomicI64::new(3), max_threads: AtomicU64::new(8), part_size: AtomicU32::new(0x10000), + dropped: AtomicBool::new(false), })) } } @@ -325,6 +334,12 @@ impl DownloaderIn *self.status.get_ref() == DownloaderStatus::Downloaded } + #[inline] + /// Return true if outter [Downloader] dropped + pub fn is_dropped(&self) -> bool { + self.dropped.qload() + } + #[inline] /// Returns true if the downloader is panic. pub fn is_panic(&self) -> bool { @@ -362,6 +377,12 @@ impl DownloaderIn self.status.replace_with2(DownloaderStatus::Downloaded) } + #[inline] + /// Set outter [Downloader] dropped + pub fn set_dropped(&self) { + self.dropped.qstore(true); + } + #[inline] /// Set the downloader is panic and set the error. /// * `err` - Error @@ -433,6 +454,9 @@ impl DownloaderIn } /// A file downloader +/// +/// When dropping downloader, the downloader need some times to let all threads exited. +/// This process is handled after [Drop]. pub struct Downloader { /// internal type downloader: Arc>, @@ -645,6 +669,16 @@ impl Do define_downloader_fn!(is_panic, bool, "Returns true if the downloader is panic."); } +impl Drop for Downloader { + fn drop(&mut self) { + self.downloader.set_dropped(); + #[cfg(test)] + { + println!("The downloader is dropped."); + } + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn test_downloader() { let p = Path::new("./test"); @@ -721,3 +755,56 @@ async fn test_failed_downloader() { } } } + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_downloader_dropped() { + let p = Path::new("./test"); + if !p.exists() { + let re = create_dir("./test"); + assert!(re.is_ok() || p.exists()); + } + let url = "https://i.pximg.net/img-original/img/2022/06/15/14/44/22/99066680_p0.png"; + let pb = p.join("99066680_p0.png"); + { + let downloader = Downloader::::new( + url, + json::object! {"referer": "https://www.pixiv.net/"}, + Some(&pb), + Some(true), + ) + .unwrap(); + match downloader { + DownloaderResult::Ok(v) => { + assert_eq!(v.is_created(), true); + v.disable_progress_bar(); + v.download(); + } + DownloaderResult::Canceled => { + panic!("This should not happened.") + } + } + } + tokio::time::sleep(Duration::new(1, 0)).await; + { + println!("The new downloader is created."); + let downloader = Downloader::::new( + url, + json::object! {"referer": "https://www.pixiv.net/"}, + Some(&pb), + Some(false), + ) + .unwrap(); + match downloader { + DownloaderResult::Ok(v) => { + assert_eq!(v.is_created(), true); + v.disable_progress_bar(); + v.download(); + v.join().await.unwrap(); + assert_eq!(v.is_downloaded(), true); + } + DownloaderResult::Canceled => { + println!("The file is already downloaded. (Too fast network. QaQ)"); + } + } + } +} diff --git a/src/downloader/enums.rs b/src/downloader/enums.rs index 8622a31..fca7f5a 100644 --- a/src/downloader/enums.rs +++ b/src/downloader/enums.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + #[derive(Debug)] /// The result when try create a new [super::Downloader] interface pub enum DownloaderResult { @@ -19,3 +21,14 @@ pub enum DownloaderStatus { /// The downloader is stoped Panic, } + +impl Display for DownloaderStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::Created => "Created", + Self::Downloading => "Downloading", + Self::Downloaded => "Downloaded", + Self::Panic => "Panic", + }) + } +} diff --git a/src/downloader/pd_file/file.rs b/src/downloader/pd_file/file.rs index 728ef68..d8d5538 100644 --- a/src/downloader/pd_file/file.rs +++ b/src/downloader/pd_file/file.rs @@ -505,14 +505,26 @@ impl PdFile { impl Drop for PdFile { fn drop(&mut self) { + #[cfg(test)] + { + println!("Is memory only: {}", self.is_mem_only()); + } if self.is_mem_only() { return; } + #[cfg(test)] + { + println!("Is completed: {}", self.is_completed()); + } if self.is_completed() { self.force_close(); self.remove_pd_file_with_err_msg(); return; } + #[cfg(test)] + { + println!("Is need saved: {}", self.is_need_saved()); + } if self.is_need_saved() { match self.write() { Ok(_) => {} diff --git a/src/downloader/tasks.rs b/src/downloader/tasks.rs index d1e3c74..9c57b04 100644 --- a/src/downloader/tasks.rs +++ b/src/downloader/tasks.rs @@ -16,6 +16,27 @@ use std::ops::Deref; use std::sync::Arc; use std::time::Duration; +/// Return [Ok(())] if the [super::Downloader] is dropped. +macro_rules! check_dropped { + ($d:expr) => { + if $d.is_dropped() { + #[cfg(test)] + { + println!("The downloader status: {}", $d.get_status()); + if $d.is_downloading() { + println!( + "The downloader size: {} / {}", + $d.pd.get_downloaded_file_size(), + $d.pd.get_file_size() + ); + } + println!("The downloader is already dropped. Exit download."); + } + return Ok(()); + } + }; +} + /// Create a download tasks in simple thread mode. pub async fn create_download_tasks_simple< T: Seek + Write + Send + Sync + ClearFile + GetTargetFileName, @@ -129,6 +150,7 @@ pub async fn handle_download match s { @@ -203,6 +225,7 @@ pub async fn check_tasks< d.add_task(task); } loop { + check_dropped!(d); tokio::time::sleep(Duration::new(0, 10_000_000)).await; let mut need_break = false; let mut dur = None;