chore(download agent): moved to completed index arr to help

serialization
This commit is contained in:
DecDuck
2024-12-21 19:21:15 +11:00
parent 3d60fd50d8
commit 64ebc191bf
3 changed files with 64 additions and 94 deletions

View File

@ -31,7 +31,8 @@ pub struct GameDownloadAgent {
pub version: String, pub version: String,
pub control_flag: DownloadThreadControl, pub control_flag: DownloadThreadControl,
pub base_dir: String, pub base_dir: String,
contexts: Mutex<Vec<DropDownloadContext>>, contexts: Vec<DropDownloadContext>,
completed_contexts: Mutex<Vec<usize>>,
pub manifest: Mutex<Option<DropManifest>>, pub manifest: Mutex<Option<DropManifest>>,
pub progress: Arc<ProgressObject>, pub progress: Arc<ProgressObject>,
sender: Sender<DownloadManagerSignal>, sender: Sender<DownloadManagerSignal>,
@ -56,7 +57,7 @@ impl Display for GameDownloadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
GameDownloadError::Communication(error) => write!(f, "{}", error), 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::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::Checksum => write!(f, "Checksum failed to validate for download"),
GameDownloadError::IoError(error) => write!(f, "{}", error), 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 { impl GameDownloadAgent {
pub fn new( pub fn new(
id: String, id: String,
@ -88,38 +97,15 @@ impl GameDownloadAgent {
control_flag, control_flag,
manifest: Mutex::new(None), manifest: Mutex::new(None),
base_dir: data_base_dir_path.to_str().unwrap().to_owned(), 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())), progress: Arc::new(ProgressObject::new(0, 0, sender.clone())),
sender, sender,
} }
} }
pub fn from_contexts(
id: String,
version: String,
base_dir: String,
manifest: DropManifest,
contexts: Vec<DropDownloadContext>,
sender: Sender<DownloadManagerSignal>,
) -> 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 // Blocking
pub fn setup_download(&self) -> Result<(), GameDownloadError> { pub fn setup_download(&mut self) -> Result<(), GameDownloadError> {
self.ensure_manifest_exists()?; self.ensure_manifest_exists()?;
info!("Ensured manifest exists"); info!("Ensured manifest exists");
@ -132,7 +118,7 @@ impl GameDownloadAgent {
} }
// Blocking // Blocking
pub fn download(&self) -> Result<(), GameDownloadError> { pub fn download(&mut self) -> Result<(), GameDownloadError> {
self.setup_download()?; self.setup_download()?;
self.set_progress_object_params(); self.set_progress_object_params();
let timer = Instant::now(); let timer = Instant::now();
@ -200,10 +186,9 @@ impl GameDownloadAgent {
return; return;
} }
let lock = self.contexts.lock().unwrap(); let length = self.contexts.len();
let length = lock.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); debug!("Setting ProgressObject max to {}", chunk_count);
self.progress.set_max(chunk_count); self.progress.set_max(chunk_count);
@ -213,18 +198,16 @@ impl GameDownloadAgent {
self.progress.set_time_now(); self.progress.set_time_now();
} }
pub fn ensure_contexts(&self) -> Result<(), GameDownloadError> { pub fn ensure_contexts(&mut self) -> Result<(), GameDownloadError> {
let context_lock = self.contexts.lock().unwrap(); if !self.contexts.is_empty() {
if !context_lock.is_empty() {
return Ok(()); return Ok(());
} }
drop(context_lock);
self.generate_contexts()?; self.generate_contexts()?;
Ok(()) 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 manifest = self.manifest.lock().unwrap().clone().unwrap();
let game_id = self.id.clone(); let game_id = self.id.clone();
@ -261,13 +244,9 @@ impl GameDownloadAgent {
let _ = fallocate(file, FallocateFlags::empty(), 0, running_offset); let _ = fallocate(file, FallocateFlags::empty(), 0, running_offset);
} }
} }
self.contexts = contexts;
if let Ok(mut context_lock) = self.contexts.lock() { Ok(())
*context_lock = contexts;
return Ok(());
}
Err(GameDownloadError::Setup(SetupError::Context))
} }
pub fn run(&self) -> Result<(), ()> { pub fn run(&self) -> Result<(), ()> {
@ -283,9 +262,14 @@ impl GameDownloadAgent {
let completed_indexes_loop_arc = completed_indexes.clone(); let completed_indexes_loop_arc = completed_indexes.clone();
pool.scope(move |scope| { 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 context = context.clone();
let control_flag = self.control_flag.clone(); // Clone arcs let control_flag = self.control_flag.clone(); // Clone arcs
let progress = self.progress.get(index); // 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 = self.completed_contexts.lock().unwrap();
let mut completed_lock = completed_indexes.lock().unwrap(); let newly_completed_lock = completed_indexes.lock().unwrap();
// Sort desc so we don't have to modify indexes completed_lock.extend(newly_completed_lock.iter());
completed_lock.sort_by(|a, b| b.cmp(a));
for index in completed_lock.iter() {
context_lock.remove(*index);
}
// If we're not out of contexts, we're not done, so we don't fire completed // 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()); info!("da for {} exited without completing", self.id.clone());
return Ok(()); return Ok(());
} }
@ -333,25 +312,3 @@ impl GameDownloadAgent {
Ok(()) Ok(())
} }
} }
#[derive(Serialize, Deserialize)]
pub struct GameDownloadAgentOfflineState {
id: String,
version: String,
base_dir: String,
manifest: DropManifest,
contexts: Vec<DropDownloadContext>,
}
impl GameDownloadAgentOfflineState {
fn to_download_agent(self, sender: Sender<DownloadManagerSignal>) -> GameDownloadAgent {
GameDownloadAgent::from_contexts(
self.id,
self.version,
self.base_dir,
self.manifest,
self.contexts,
sender,
)
}
}

View File

@ -68,7 +68,7 @@ Behold, my madness - quexeky
pub type CurrentProgressObject = Arc<Mutex<Option<Arc<ProgressObject>>>>; pub type CurrentProgressObject = Arc<Mutex<Option<Arc<ProgressObject>>>>;
pub struct DownloadManagerBuilder { pub struct DownloadManagerBuilder {
download_agent_registry: HashMap<String, Arc<GameDownloadAgent>>, download_agent_registry: HashMap<String, Arc<Mutex<GameDownloadAgent>>>,
download_queue: Queue, download_queue: Queue,
command_receiver: Receiver<DownloadManagerSignal>, command_receiver: Receiver<DownloadManagerSignal>,
sender: Sender<DownloadManagerSignal>, sender: Sender<DownloadManagerSignal>,
@ -156,7 +156,7 @@ impl DownloadManagerBuilder {
fn sync_download_agent(&self) {} fn sync_download_agent(&self) {}
fn remove_and_cleanup_game(&mut self, game_id: &String) -> Arc<GameDownloadAgent> { fn remove_and_cleanup_game(&mut self, game_id: &String) -> Arc<Mutex<GameDownloadAgent>> {
self.download_queue.pop_front(); self.download_queue.pop_front();
let download_agent = self.download_agent_registry.remove(game_id).unwrap(); let download_agent = self.download_agent_registry.remove(game_id).unwrap();
self.cleanup_current_download(); self.cleanup_current_download();
@ -227,13 +227,16 @@ impl DownloadManagerBuilder {
if interface.id == game_id { if interface.id == game_id {
info!("Popping consumed data"); info!("Popping consumed data");
let download_agent = self.remove_and_cleanup_game(&game_id); 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( let version = download_agent_lock.version.clone();
game_id, let install_dir = download_agent_lock.base_dir.clone();
download_agent.version.clone(),
download_agent.base_dir.clone(), drop(download_agent_lock);
&self.app_handle,
) { if let Err(error) =
on_game_complete(game_id, version, install_dir, &self.app_handle)
{
self.sender self.sender
.send(DownloadManagerSignal::Error( .send(DownloadManagerSignal::Error(
GameDownloadError::Communication(error), GameDownloadError::Communication(error),
@ -248,19 +251,24 @@ impl DownloadManagerBuilder {
fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) { fn manage_queue_signal(&mut self, id: String, version: String, target_download_dir: usize) {
info!("Got signal Queue"); info!("Got signal Queue");
let download_agent = Arc::new(GameDownloadAgent::new( let download_agent = Arc::new(Mutex::new(GameDownloadAgent::new(
id.clone(), id.clone(),
version, version,
target_download_dir, target_download_dir,
self.sender.clone(), self.sender.clone(),
)); )));
let download_agent_lock = download_agent.lock().unwrap();
let agent_status = GameDownloadStatus::Queued; let agent_status = GameDownloadStatus::Queued;
let interface_data = GameDownloadAgentQueueStandin { let interface_data = GameDownloadAgentQueueStandin {
id: id.clone(), id: id.clone(),
status: Mutex::new(agent_status), 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 self.download_agent_registry
.insert(interface_data.id.clone(), download_agent); .insert(interface_data.id.clone(), download_agent);
self.download_queue.append(interface_data); self.download_queue.append(interface_data);
@ -287,24 +295,28 @@ impl DownloadManagerBuilder {
.get(&agent_data.id) .get(&agent_data.id)
.unwrap() .unwrap()
.clone(); .clone();
let download_agent_lock = download_agent.lock().unwrap();
self.current_download_agent = Some(agent_data); self.current_download_agent = Some(agent_data);
// Cloning option should be okay because it only clones the Arc inside, not the AgentInterfaceData // 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 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); *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()); self.active_control_flag = Some(active_control_flag.clone());
let sender = self.sender.clone(); let sender = self.sender.clone();
drop(download_agent_lock);
info!("Spawning download"); info!("Spawning download");
let mut download_thread_lock = self.current_download_thread.lock().unwrap(); let mut download_thread_lock = self.current_download_thread.lock().unwrap();
*download_thread_lock = Some(spawn(move || { *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 // Returns once we've exited the download
// (not necessarily completed) // (not necessarily completed)
// The download agent will fire the completed event for us // The download agent will fire the completed event for us
@ -315,6 +327,7 @@ impl DownloadManagerBuilder {
sender.send(DownloadManagerSignal::Error(err)).unwrap(); sender.send(DownloadManagerSignal::Error(err)).unwrap();
} }
}; };
drop(download_agent_lock);
})); }));
// Set status for games // Set status for games
@ -347,7 +360,7 @@ impl DownloadManagerBuilder {
*lock = GameDownloadStatus::Error; *lock = GameDownloadStatus::Error;
self.set_status(DownloadManagerStatus::Error(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.set_game_status(game_id, DatabaseGameStatus::Remote {});
self.sender.send(DownloadManagerSignal::Update).unwrap(); self.sender.send(DownloadManagerSignal::Update).unwrap();

View File

@ -234,7 +234,7 @@ pub fn run() {
], ],
)?; )?;
let tray = TrayIconBuilder::new() TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone()) .icon(app.default_window_icon().unwrap().clone())
.menu(&menu) .menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() { .on_menu_event(|app, event| match event.id.as_ref() {