chore: Progress on rolling progress window

This commit is contained in:
quexeky
2025-01-19 08:41:20 +11:00
parent 5f5cbd07c6
commit cf19477d4d
8 changed files with 74 additions and 60 deletions

7
src-tauri/Cargo.lock generated
View File

@ -255,6 +255,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "atomic-instant-full"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6541700e074cda41b1c6f98c2cae6cde819967bf142078f069cad85387cdbe"
[[package]]
name = "atomic-waker"
version = "1.1.2"
@ -1033,6 +1039,7 @@ dependencies = [
name = "drop-app"
version = "0.1.0"
dependencies = [
"atomic-instant-full",
"boxcar",
"chrono",
"derive_builder",

View File

@ -52,6 +52,7 @@ throttle_my_fn = "0.2.6"
parking_lot = "0.12.3"
merge-struct = "0.1.0"
serde_merge = "0.1.3"
atomic-instant-full = "0.1.0"
[dependencies.tauri]
version = "2.1.1"

View File

@ -1,4 +1,4 @@
use log::{debug, info};
use log::{debug, error};
use tauri::AppHandle;
use crate::AppState;
@ -12,7 +12,12 @@ pub fn cleanup_and_exit(app: &AppHandle, state: &tauri::State<'_, std::sync::Mut
debug!("Cleaning up and exiting application");
let download_manager = state.lock().unwrap().download_manager.clone();
match download_manager.ensure_terminated() {
Ok(_) => {},
Ok(res) => {
match res {
Ok(_) => debug!("Download manager terminated correctly"),
Err(_) => error!("Download manager failed to terminate correctly"),
}
},
Err(e) => panic!("{:?}", e),
}

View File

@ -7,10 +7,11 @@ use std::{
time::{Duration, Instant},
};
use atomic_instant_full::AtomicInstant;
use log::info;
use throttle_my_fn::throttle;
use super::download_manager::DownloadManagerSignal;
use super::{download_manager::DownloadManagerSignal, rolling_progress_updates::RollingProgressWindow};
#[derive(Clone)]
pub struct ProgressObject {
@ -20,8 +21,10 @@ pub struct ProgressObject {
sender: Sender<DownloadManagerSignal>,
points_towards_update: Arc<AtomicUsize>,
points_to_push_update: Arc<AtomicUsize>,
last_update: Arc<RwLock<Instant>>,
amount_last_update: Arc<AtomicUsize>,
//last_update: Arc<RwLock<Instant>>,
last_update_time: Arc<AtomicInstant>,
bytes_last_update: Arc<AtomicUsize>,
rolling: RollingProgressWindow<128>
}
pub struct ProgressHandle {
@ -42,7 +45,7 @@ impl ProgressHandle {
pub fn add(&self, amount: usize) {
self.progress
.fetch_add(amount, std::sync::atomic::Ordering::Relaxed);
self.progress_object.check_push_update(amount);
push_update(&self.progress_object, amount);
}
}
@ -61,50 +64,13 @@ impl ProgressObject {
points_towards_update: Arc::new(AtomicUsize::new(0)),
points_to_push_update: Arc::new(AtomicUsize::new(points_to_push_update)),
last_update: Arc::new(RwLock::new(Instant::now())),
amount_last_update: Arc::new(AtomicUsize::new(0)),
last_update_time: Arc::new(AtomicInstant::now()),
bytes_last_update: Arc::new(AtomicUsize::new(0)),
rolling: RollingProgressWindow::new(),
}
}
pub fn check_push_update(&self, amount_added: usize) {
let current_amount = self
.points_towards_update
.fetch_add(amount_added, Ordering::Relaxed);
let to_update = self.points_to_push_update.fetch_add(0, Ordering::Relaxed);
if current_amount >= to_update {
self.points_towards_update
.fetch_sub(to_update, Ordering::Relaxed);
update_queue(self);
}
let last_update = self.last_update.read().unwrap();
let last_update_difference = Instant::now().duration_since(*last_update).as_millis();
if last_update_difference > 1000 {
// push update
drop(last_update);
let mut last_update = self.last_update.write().unwrap();
*last_update = Instant::now();
drop(last_update);
let current_amount = self.sum();
let max = self.get_max();
let amount_at_last_update = self.amount_last_update.fetch_add(0, Ordering::Relaxed);
self.amount_last_update
.store(current_amount, Ordering::Relaxed);
let amount_since_last_update = current_amount - amount_at_last_update;
let kilobytes_per_second =
amount_since_last_update / (last_update_difference as usize).max(1);
let remaining = max - current_amount; // bytes
let time_remaining = (remaining / 1000) / kilobytes_per_second.max(1);
update_ui(self, kilobytes_per_second, time_remaining);
}
}
pub fn set_time_now(&self) {
*self.start.lock().unwrap() = Instant::now();
@ -136,23 +102,48 @@ impl ProgressObject {
pub fn get(&self, index: usize) -> Arc<AtomicUsize> {
self.progress_instances.lock().unwrap()[index].clone()
}
fn update_window(&self, kilobytes_per_second: usize) {
self.rolling.update(kilobytes_per_second);
}
}
#[throttle(50, Duration::from_secs(1))]
#[throttle(1, Duration::from_millis(100))]
fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) {
progress_object
.sender
.send(DownloadManagerSignal::UpdateUIStats(
kilobytes_per_second,
progress_object.rolling.get_average(),
time_remaining,
))
.unwrap();
}
#[throttle(50, Duration::from_secs(1))]
#[throttle(1, Duration::from_millis(100))]
fn update_queue(progress: &ProgressObject) {
progress
.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
#[throttle(1, Duration::from_millis(20))]
pub fn push_update(progress: &ProgressObject, amount_added: usize) {
let last_update_time = progress.last_update_time.swap(Instant::now(), Ordering::SeqCst);
let time_since_last_update = Instant::now().duration_since(last_update_time).as_millis();
let current_bytes_downloaded = progress.sum();
let max = progress.get_max();
let bytes_at_last_update = progress.bytes_last_update.swap(current_bytes_downloaded, Ordering::Relaxed);
let bytes_since_last_update = current_bytes_downloaded - bytes_at_last_update;
let kilobytes_per_second =
bytes_since_last_update / (time_since_last_update as usize).max(1);
let bytes_remaining = max - current_bytes_downloaded; // bytes
let time_remaining = (bytes_remaining / 1000) / kilobytes_per_second.max(1);
progress.update_window(kilobytes_per_second);
update_ui(progress, kilobytes_per_second, time_remaining);
}

View File

@ -9,12 +9,18 @@ pub struct RollingProgressWindow<const S: usize> {
current: Arc<AtomicUsize>,
}
impl<const S: usize> RollingProgressWindow<S> {
pub fn new() -> Self {
Self {
window: Arc::new([(); S].map(|_| AtomicUsize::new(0))),
current: Arc::new(AtomicUsize::new(0))
}
}
pub fn update(&self, kilobytes_per_second: usize) {
let index = self.current.fetch_add(1, Ordering::SeqCst);
let current = &self.window[index % S];
current.store(kilobytes_per_second, Ordering::Release);
}
pub fn get_average(&self) -> usize {
self.window.iter().map(|x| x.load(Ordering::Relaxed)).sum()
self.window.iter().map(|x| x.load(Ordering::Relaxed)).sum::<usize>() / S
}
}

View File

@ -45,6 +45,7 @@ use remote::commands::{
auth_initiate, gen_drop_url, manual_recieve_handshake, retry_connect, sign_out, use_remote,
};
use serde::{Deserialize, Serialize};
use tauri::ipc::IpcResponse;
use std::path::Path;
use std::sync::Arc;
use std::{
@ -136,7 +137,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
debug!("Database is set up");
// TODO: Account for possible failure
let (app_status, user) = auth::setup().unwrap();
let (app_status, user) = auth::setup();
let db_handle = DB.borrow_data().unwrap();
let mut missing_games = Vec::new();
@ -315,7 +316,7 @@ pub fn run() {
app.webview_windows().get("main").unwrap().show().unwrap();
}
"quit" => {
cleanup_and_exit(app, &state);
cleanup_and_exit(app, &app.state());
}
_ => {

View File

@ -180,15 +180,19 @@ pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> {
Ok(())
}
pub fn setup() -> Result<(AppStatus, Option<User>), RemoteAccessError> {
pub fn setup() -> (AppStatus, Option<User>) {
let data = DB.borrow_data().unwrap();
let auth = data.auth.clone();
drop(data);
if auth.is_some() {
let user_result = fetch_user()?;
return Ok((AppStatus::SignedIn, Some(user_result)));
let user_result = match fetch_user() {
Ok(data) => data,
Err(RemoteAccessError::FetchError(_)) => return (AppStatus::ServerUnavailable, None),
Err(_) => return (AppStatus::SignedInNeedsReauth, None),
};
return (AppStatus::SignedIn, Some(user_result));
}
Ok((AppStatus::SignedOut, None))
(AppStatus::SignedOut, None)
}

View File

@ -57,14 +57,13 @@ pub fn sign_out(app: AppHandle) {
}
#[tauri::command]
pub fn retry_connect(state: tauri::State<'_, Mutex<AppState>>) -> UserValue<(), RemoteAccessError> {
let (app_status, user) = setup()?;
pub fn retry_connect(state: tauri::State<'_, Mutex<AppState>>) {
let (app_status, user) = setup();
let mut guard = state.lock().unwrap();
guard.status = app_status;
guard.user = user;
drop(guard);
UserValue::Ok(())
}
#[tauri::command]