blob: e25a04474f1d5504984f4ac2f2b9403f827ccfe3 [file] [log] [blame] [edit]
// 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)
}
}