| // Copyright 2023 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // This module creates thread-safe DlcQueue | |
| use log::debug; | |
| use std::collections::{HashSet, VecDeque}; | |
| use std::sync::Arc; | |
| use tokio::sync::RwLock; | |
| use crate::common::{SteamAppId, MAX_CONCURRENT_DLC_INSTALLS, MAX_INSTALL_QUEUE_SIZE}; | |
| #[derive(Debug)] | |
| pub struct DlcQueue { | |
| // LIFO queue - latest installations should be prioritized over older | |
| // installation requests because on game launch, installations are | |
| // requested. | |
| install_queue: VecDeque<SteamAppId>, | |
| // Uninstall queue does not matter whether it is FIFO or LIFO, they do not | |
| // have to be prioritized. However, it is using VecQueue so that we can | |
| // reuse utility functions. | |
| uninstall_queue: VecDeque<SteamAppId>, | |
| // Currently installing set of games. | |
| installing: HashSet<SteamAppId>, | |
| } | |
| pub type DlcQueuePtr = Arc<RwLock<DlcQueue>>; | |
| fn remove_from_queue<T: std::cmp::PartialEq>(queue: &mut VecDeque<T>, to_remove: &T) { | |
| if let Some(index) = queue.iter().position(|item| *item == *to_remove) { | |
| queue.remove(index); | |
| } | |
| } | |
| impl DlcQueue { | |
| pub fn queue_install(self: &mut DlcQueue, steam_app_id: &SteamAppId) -> Option<SteamAppId> { | |
| if self.installing.contains(steam_app_id) { | |
| return None; | |
| } | |
| remove_from_queue(&mut self.uninstall_queue, steam_app_id); | |
| remove_from_queue(&mut self.install_queue, steam_app_id); | |
| // Put the item to the front of the vector, so that pop_front() returns | |
| // it (LIFO). | |
| self.install_queue.push_front(*steam_app_id); | |
| if self.install_queue.len() > MAX_INSTALL_QUEUE_SIZE { | |
| if let Some(removed) = self.install_queue.pop_back() { | |
| debug!("Max install queue size reached, removed {}", removed); | |
| return Some(removed); | |
| } | |
| } | |
| None | |
| } | |
| pub fn next_to_install(self: &mut DlcQueue) -> Option<SteamAppId> { | |
| if let Some(app_id) = self.install_queue.pop_front() { | |
| // Add the item to installing set, so that we know what is being | |
| // installed preemptively. | |
| self.installing.insert(app_id); | |
| return Some(app_id); | |
| } | |
| None | |
| } | |
| pub fn available_to_install_more_dlc(self: &DlcQueue) -> bool { | |
| (MAX_CONCURRENT_DLC_INSTALLS - self.installing.len()) > 0 | |
| } | |
| pub fn remove_installing(self: &mut DlcQueue, steam_app_id: &SteamAppId) -> bool { | |
| self.installing.remove(steam_app_id) | |
| } | |
| pub fn queue_uninstall_multi(self: &mut DlcQueue, ids: &HashSet<SteamAppId>) { | |
| for steam_app_id in ids { | |
| self.installing.remove(steam_app_id); | |
| } | |
| for steam_app_id in ids { | |
| remove_from_queue(&mut self.install_queue, steam_app_id); | |
| } | |
| self.uninstall_queue.extend(ids); | |
| } | |
| pub fn queue_uninstall(self: &mut DlcQueue, steam_app_id: &SteamAppId) { | |
| self.remove_installing(steam_app_id); | |
| remove_from_queue(&mut self.install_queue, steam_app_id); | |
| self.uninstall_queue.push_front(*steam_app_id); | |
| } | |
| pub fn next_to_uninstall(self: &mut DlcQueue) -> Option<SteamAppId> { | |
| self.uninstall_queue.pop_front() | |
| } | |
| } | |
| impl std::fmt::Display for DlcQueue { | |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
| f.write_fmt(format_args!( | |
| "DlcQueue{{ install_queue:{:?} installing:{:?} uninstall_queue:{:?} }}", | |
| self.install_queue, self.installing, self.uninstall_queue, | |
| ))?; | |
| Ok(()) | |
| } | |
| } | |
| pub fn new_queue() -> DlcQueuePtr { | |
| Arc::new(RwLock::new(DlcQueue { | |
| install_queue: VecDeque::new(), | |
| installing: HashSet::new(), | |
| uninstall_queue: VecDeque::new(), | |
| })) | |
| } | |
| #[cfg(test)] | |
| impl DlcQueue { | |
| pub fn get_install_queue(&self) -> &VecDeque<SteamAppId> { | |
| &self.install_queue | |
| } | |
| pub fn get_uninstall_queue(&self) -> &VecDeque<SteamAppId> { | |
| &self.uninstall_queue | |
| } | |
| pub fn get_installing_set(&self) -> &HashSet<SteamAppId> { | |
| &self.installing | |
| } | |
| pub fn clear_installing(&mut self) { | |
| self.installing.clear(); | |
| } | |
| pub fn add_installing(self: &mut DlcQueue, steam_app_id: &SteamAppId) -> bool { | |
| self.installing.insert(*steam_app_id) | |
| } | |
| } |