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
View File
@@ -255,6 +255,12 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "atomic-instant-full"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6541700e074cda41b1c6f98c2cae6cde819967bf142078f069cad85387cdbe"
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@@ -1033,6 +1039,7 @@ dependencies = [
name = "drop-app" name = "drop-app"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"atomic-instant-full",
"boxcar", "boxcar",
"chrono", "chrono",
"derive_builder", "derive_builder",
+1
View File
@@ -52,6 +52,7 @@ throttle_my_fn = "0.2.6"
parking_lot = "0.12.3" parking_lot = "0.12.3"
merge-struct = "0.1.0" merge-struct = "0.1.0"
serde_merge = "0.1.3" serde_merge = "0.1.3"
atomic-instant-full = "0.1.0"
[dependencies.tauri] [dependencies.tauri]
version = "2.1.1" version = "2.1.1"
+7 -2
View File
@@ -1,4 +1,4 @@
use log::{debug, info}; use log::{debug, error};
use tauri::AppHandle; use tauri::AppHandle;
use crate::AppState; 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"); debug!("Cleaning up and exiting application");
let download_manager = state.lock().unwrap().download_manager.clone(); let download_manager = state.lock().unwrap().download_manager.clone();
match download_manager.ensure_terminated() { 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), Err(e) => panic!("{:?}", e),
} }
@@ -7,10 +7,11 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use atomic_instant_full::AtomicInstant;
use log::info; use log::info;
use throttle_my_fn::throttle; use throttle_my_fn::throttle;
use super::download_manager::DownloadManagerSignal; use super::{download_manager::DownloadManagerSignal, rolling_progress_updates::RollingProgressWindow};
#[derive(Clone)] #[derive(Clone)]
pub struct ProgressObject { pub struct ProgressObject {
@@ -20,8 +21,10 @@ pub struct ProgressObject {
sender: Sender<DownloadManagerSignal>, sender: Sender<DownloadManagerSignal>,
points_towards_update: Arc<AtomicUsize>, points_towards_update: Arc<AtomicUsize>,
points_to_push_update: Arc<AtomicUsize>, points_to_push_update: Arc<AtomicUsize>,
last_update: Arc<RwLock<Instant>>, //last_update: Arc<RwLock<Instant>>,
amount_last_update: Arc<AtomicUsize>, last_update_time: Arc<AtomicInstant>,
bytes_last_update: Arc<AtomicUsize>,
rolling: RollingProgressWindow<128>
} }
pub struct ProgressHandle { pub struct ProgressHandle {
@@ -42,7 +45,7 @@ impl ProgressHandle {
pub fn add(&self, amount: usize) { pub fn add(&self, amount: usize) {
self.progress self.progress
.fetch_add(amount, std::sync::atomic::Ordering::Relaxed); .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_towards_update: Arc::new(AtomicUsize::new(0)),
points_to_push_update: Arc::new(AtomicUsize::new(points_to_push_update)), points_to_push_update: Arc::new(AtomicUsize::new(points_to_push_update)),
last_update: Arc::new(RwLock::new(Instant::now())), last_update_time: Arc::new(AtomicInstant::now()),
amount_last_update: Arc::new(AtomicUsize::new(0)), 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) { pub fn set_time_now(&self) {
*self.start.lock().unwrap() = Instant::now(); *self.start.lock().unwrap() = Instant::now();
@@ -136,23 +102,48 @@ impl ProgressObject {
pub fn get(&self, index: usize) -> Arc<AtomicUsize> { pub fn get(&self, index: usize) -> Arc<AtomicUsize> {
self.progress_instances.lock().unwrap()[index].clone() 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) { fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) {
progress_object progress_object
.sender .sender
.send(DownloadManagerSignal::UpdateUIStats( .send(DownloadManagerSignal::UpdateUIStats(
kilobytes_per_second, progress_object.rolling.get_average(),
time_remaining, time_remaining,
)) ))
.unwrap(); .unwrap();
} }
#[throttle(50, Duration::from_secs(1))] #[throttle(1, Duration::from_millis(100))]
fn update_queue(progress: &ProgressObject) { fn update_queue(progress: &ProgressObject) {
progress progress
.sender .sender
.send(DownloadManagerSignal::UpdateUIQueue) .send(DownloadManagerSignal::UpdateUIQueue)
.unwrap(); .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);
}
@@ -9,12 +9,18 @@ pub struct RollingProgressWindow<const S: usize> {
current: Arc<AtomicUsize>, current: Arc<AtomicUsize>,
} }
impl<const S: usize> RollingProgressWindow<S> { 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) { pub fn update(&self, kilobytes_per_second: usize) {
let index = self.current.fetch_add(1, Ordering::SeqCst); let index = self.current.fetch_add(1, Ordering::SeqCst);
let current = &self.window[index % S]; let current = &self.window[index % S];
current.store(kilobytes_per_second, Ordering::Release); current.store(kilobytes_per_second, Ordering::Release);
} }
pub fn get_average(&self) -> usize { 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
} }
} }
+3 -2
View File
@@ -45,6 +45,7 @@ use remote::commands::{
auth_initiate, gen_drop_url, manual_recieve_handshake, retry_connect, sign_out, use_remote, auth_initiate, gen_drop_url, manual_recieve_handshake, retry_connect, sign_out, use_remote,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tauri::ipc::IpcResponse;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::{ use std::{
@@ -136,7 +137,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
debug!("Database is set up"); debug!("Database is set up");
// TODO: Account for possible failure // 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 db_handle = DB.borrow_data().unwrap();
let mut missing_games = Vec::new(); let mut missing_games = Vec::new();
@@ -315,7 +316,7 @@ pub fn run() {
app.webview_windows().get("main").unwrap().show().unwrap(); app.webview_windows().get("main").unwrap().show().unwrap();
} }
"quit" => { "quit" => {
cleanup_and_exit(app, &state); cleanup_and_exit(app, &app.state());
} }
_ => { _ => {
+8 -4
View File
@@ -180,15 +180,19 @@ pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> {
Ok(()) Ok(())
} }
pub fn setup() -> Result<(AppStatus, Option<User>), RemoteAccessError> { pub fn setup() -> (AppStatus, Option<User>) {
let data = DB.borrow_data().unwrap(); let data = DB.borrow_data().unwrap();
let auth = data.auth.clone(); let auth = data.auth.clone();
drop(data); drop(data);
if auth.is_some() { if auth.is_some() {
let user_result = fetch_user()?; let user_result = match fetch_user() {
return Ok((AppStatus::SignedIn, Some(user_result))); 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)
} }
+2 -3
View File
@@ -57,14 +57,13 @@ pub fn sign_out(app: AppHandle) {
} }
#[tauri::command] #[tauri::command]
pub fn retry_connect(state: tauri::State<'_, Mutex<AppState>>) -> UserValue<(), RemoteAccessError> { pub fn retry_connect(state: tauri::State<'_, Mutex<AppState>>) {
let (app_status, user) = setup()?; let (app_status, user) = setup();
let mut guard = state.lock().unwrap(); let mut guard = state.lock().unwrap();
guard.status = app_status; guard.status = app_status;
guard.user = user; guard.user = user;
drop(guard); drop(guard);
UserValue::Ok(())
} }
#[tauri::command] #[tauri::command]