diff --git a/src-tauri/src/downloads/download_agent.rs b/src-tauri/src/downloads/download_agent.rs index 9930a83..5a6fad7 100644 --- a/src-tauri/src/downloads/download_agent.rs +++ b/src-tauri/src/downloads/download_agent.rs @@ -31,7 +31,8 @@ pub struct GameDownloadAgent { pub version: String, pub control_flag: DownloadThreadControl, pub base_dir: String, - contexts: Mutex>, + contexts: Vec, + completed_contexts: Mutex>, pub manifest: Mutex>, pub progress: Arc, sender: Sender, @@ -56,7 +57,7 @@ impl Display for GameDownloadError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { GameDownloadError::Communication(error) => write!(f, "{}", error), - GameDownloadError::Setup(error) => write!(f, "{:?}", error), + GameDownloadError::Setup(error) => write!(f, "An error occurred while setting up the download: {}", error), GameDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"), GameDownloadError::Checksum => write!(f, "Checksum failed to validate for download"), GameDownloadError::IoError(error) => write!(f, "{}", error), @@ -65,6 +66,14 @@ impl Display for GameDownloadError { } } +impl Display for SetupError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + SetupError::Context => write!(f, "Failed to generate contexts for download"), + } + } +} + impl GameDownloadAgent { pub fn new( id: String, @@ -88,38 +97,15 @@ impl GameDownloadAgent { control_flag, manifest: Mutex::new(None), base_dir: data_base_dir_path.to_str().unwrap().to_owned(), - contexts: Mutex::new(Vec::new()), + contexts: Vec::new(), + completed_contexts: Mutex::new(Vec::new()), progress: Arc::new(ProgressObject::new(0, 0, sender.clone())), sender, } } - pub fn from_contexts( - id: String, - version: String, - base_dir: String, - manifest: DropManifest, - contexts: Vec, - sender: Sender, - ) -> Self { - let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop); - - let me = Self { - id, - version, - control_flag, - manifest: Mutex::new(Some(manifest)), - base_dir, - contexts: Mutex::new(contexts), - progress: Arc::new(ProgressObject::new(0, 0, sender.clone())), - sender, - }; - me.set_progress_object_params(); - me - } - // Blocking - pub fn setup_download(&self) -> Result<(), GameDownloadError> { + pub fn setup_download(&mut self) -> Result<(), GameDownloadError> { self.ensure_manifest_exists()?; info!("Ensured manifest exists"); @@ -132,7 +118,7 @@ impl GameDownloadAgent { } // Blocking - pub fn download(&self) -> Result<(), GameDownloadError> { + pub fn download(&mut self) -> Result<(), GameDownloadError> { self.setup_download()?; self.set_progress_object_params(); let timer = Instant::now(); @@ -200,10 +186,9 @@ impl GameDownloadAgent { return; } - let lock = self.contexts.lock().unwrap(); - let length = lock.len(); + let length = self.contexts.len(); - let chunk_count = lock.iter().map(|chunk| chunk.length).sum(); + let chunk_count = self.contexts.iter().map(|chunk| chunk.length).sum(); debug!("Setting ProgressObject max to {}", chunk_count); self.progress.set_max(chunk_count); @@ -213,18 +198,16 @@ impl GameDownloadAgent { self.progress.set_time_now(); } - pub fn ensure_contexts(&self) -> Result<(), GameDownloadError> { - let context_lock = self.contexts.lock().unwrap(); - if !context_lock.is_empty() { + pub fn ensure_contexts(&mut self) -> Result<(), GameDownloadError> { + if !self.contexts.is_empty() { return Ok(()); } - drop(context_lock); self.generate_contexts()?; Ok(()) } - pub fn generate_contexts(&self) -> Result<(), GameDownloadError> { + pub fn generate_contexts(&mut self) -> Result<(), GameDownloadError> { let manifest = self.manifest.lock().unwrap().clone().unwrap(); let game_id = self.id.clone(); @@ -261,13 +244,9 @@ impl GameDownloadAgent { let _ = fallocate(file, FallocateFlags::empty(), 0, running_offset); } } + self.contexts = contexts; - if let Ok(mut context_lock) = self.contexts.lock() { - *context_lock = contexts; - return Ok(()); - } - - Err(GameDownloadError::Setup(SetupError::Context)) + Ok(()) } pub fn run(&self) -> Result<(), ()> { @@ -283,9 +262,14 @@ impl GameDownloadAgent { let completed_indexes_loop_arc = completed_indexes.clone(); pool.scope(move |scope| { - let contexts = self.contexts.lock().unwrap(); + let completed_lock = self.completed_contexts.lock().unwrap(); + + for (index, context) in self.contexts.iter().enumerate() { + // If we've done this one already, skip it + if completed_lock.contains(&index) { + continue; + } - for (index, context) in contexts.iter().enumerate() { let context = context.clone(); let control_flag = self.control_flag.clone(); // Clone arcs let progress = self.progress.get(index); // Clone arcs @@ -309,18 +293,13 @@ impl GameDownloadAgent { } }); - let mut context_lock = self.contexts.lock().unwrap(); - let mut completed_lock = completed_indexes.lock().unwrap(); + let mut completed_lock = self.completed_contexts.lock().unwrap(); + let newly_completed_lock = completed_indexes.lock().unwrap(); - // Sort desc so we don't have to modify indexes - completed_lock.sort_by(|a, b| b.cmp(a)); - - for index in completed_lock.iter() { - context_lock.remove(*index); - } + completed_lock.extend(newly_completed_lock.iter()); // If we're not out of contexts, we're not done, so we don't fire completed - if !context_lock.is_empty() { + if completed_lock.len() != self.contexts.len() { info!("da for {} exited without completing", self.id.clone()); return Ok(()); } @@ -333,25 +312,3 @@ impl GameDownloadAgent { Ok(()) } } - -#[derive(Serialize, Deserialize)] -pub struct GameDownloadAgentOfflineState { - id: String, - version: String, - base_dir: String, - manifest: DropManifest, - contexts: Vec, -} - -impl GameDownloadAgentOfflineState { - fn to_download_agent(self, sender: Sender) -> GameDownloadAgent { - GameDownloadAgent::from_contexts( - self.id, - self.version, - self.base_dir, - self.manifest, - self.contexts, - sender, - ) - } -} diff --git a/src-tauri/src/downloads/download_manager_builder.rs b/src-tauri/src/downloads/download_manager_builder.rs index 6ccf8c8..9e42598 100644 --- a/src-tauri/src/downloads/download_manager_builder.rs +++ b/src-tauri/src/downloads/download_manager_builder.rs @@ -68,7 +68,7 @@ Behold, my madness - quexeky pub type CurrentProgressObject = Arc>>>; pub struct DownloadManagerBuilder { - download_agent_registry: HashMap>, + download_agent_registry: HashMap>>, download_queue: Queue, command_receiver: Receiver, sender: Sender, @@ -156,7 +156,7 @@ impl DownloadManagerBuilder { fn sync_download_agent(&self) {} - fn remove_and_cleanup_game(&mut self, game_id: &String) -> Arc { + fn remove_and_cleanup_game(&mut self, game_id: &String) -> Arc> { self.download_queue.pop_front(); let download_agent = self.download_agent_registry.remove(game_id).unwrap(); self.cleanup_current_download(); @@ -227,13 +227,16 @@ impl DownloadManagerBuilder { if interface.id == game_id { info!("Popping consumed data"); let download_agent = self.remove_and_cleanup_game(&game_id); + let download_agent_lock = download_agent.lock().unwrap(); - if let Err(error) = on_game_complete( - game_id, - download_agent.version.clone(), - download_agent.base_dir.clone(), - &self.app_handle, - ) { + let version = download_agent_lock.version.clone(); + let install_dir = download_agent_lock.base_dir.clone(); + + drop(download_agent_lock); + + if let Err(error) = + on_game_complete(game_id, version, install_dir, &self.app_handle) + { self.sender .send(DownloadManagerSignal::Error( GameDownloadError::Communication(error), @@ -248,19 +251,24 @@ impl DownloadManagerBuilder { fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) { info!("Got signal Queue"); - let download_agent = Arc::new(GameDownloadAgent::new( + let download_agent = Arc::new(Mutex::new(GameDownloadAgent::new( id.clone(), version, target_download_dir, self.sender.clone(), - )); + ))); + let download_agent_lock = download_agent.lock().unwrap(); + let agent_status = GameDownloadStatus::Queued; let interface_data = GameDownloadAgentQueueStandin { id: id.clone(), status: Mutex::new(agent_status), - progress: download_agent.progress.clone(), + progress: download_agent_lock.progress.clone(), }; - let version_name = download_agent.version.clone(); + let version_name = download_agent_lock.version.clone(); + + drop(download_agent_lock); + self.download_agent_registry .insert(interface_data.id.clone(), download_agent); self.download_queue.append(interface_data); @@ -287,24 +295,28 @@ impl DownloadManagerBuilder { .get(&agent_data.id) .unwrap() .clone(); + let download_agent_lock = download_agent.lock().unwrap(); self.current_download_agent = Some(agent_data); // Cloning option should be okay because it only clones the Arc inside, not the AgentInterfaceData let agent_data = self.current_download_agent.clone().unwrap(); - let version_name = download_agent.version.clone(); + let version_name = download_agent_lock.version.clone(); - let progress_object = download_agent.progress.clone(); + let progress_object = download_agent_lock.progress.clone(); *self.progress.lock().unwrap() = Some(progress_object); - let active_control_flag = download_agent.control_flag.clone(); + let active_control_flag = download_agent_lock.control_flag.clone(); self.active_control_flag = Some(active_control_flag.clone()); let sender = self.sender.clone(); + drop(download_agent_lock); + info!("Spawning download"); let mut download_thread_lock = self.current_download_thread.lock().unwrap(); *download_thread_lock = Some(spawn(move || { - match download_agent.download() { + let mut download_agent_lock = download_agent.lock().unwrap(); + match download_agent_lock.download() { // Returns once we've exited the download // (not necessarily completed) // The download agent will fire the completed event for us @@ -315,6 +327,7 @@ impl DownloadManagerBuilder { sender.send(DownloadManagerSignal::Error(err)).unwrap(); } }; + drop(download_agent_lock); })); // Set status for games @@ -347,7 +360,7 @@ impl DownloadManagerBuilder { *lock = GameDownloadStatus::Error; self.set_status(DownloadManagerStatus::Error(error)); - let game_id = self.current_download_agent.as_ref().unwrap().id.clone(); + let game_id = current_status.id.clone(); self.set_game_status(game_id, DatabaseGameStatus::Remote {}); self.sender.send(DownloadManagerSignal::Update).unwrap(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index acecd5f..6e25768 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -234,7 +234,7 @@ pub fn run() { ], )?; - let tray = TrayIconBuilder::new() + TrayIconBuilder::new() .icon(app.default_window_icon().unwrap().clone()) .menu(&menu) .on_menu_event(|app, event| match event.id.as_ref() {