From 1610f1827890f8d064ae9c00f15da4b9387b49f1 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 9 Nov 2022 22:09:05 -0500 Subject: [PATCH] Refactor API processing to apply settings from single thread and queue API calls instead of locking --- backend/Cargo.lock | 33 +++- backend/Cargo.toml | 8 +- backend/src/api/async_utils.rs | 65 ++++++++ backend/src/api/battery.rs | 62 ++++---- backend/src/api/cpu.rs | 273 ++++++++++++++++++-------------- backend/src/api/general.rs | 153 ++++++++++++------ backend/src/api/gpu.rs | 208 ++++++++++++------------ backend/src/api/handler.rs | 199 +++++++++++++++++++++++ backend/src/api/mod.rs | 2 + backend/src/api/utility.rs | 4 +- backend/src/api_worker.rs | 13 ++ backend/src/consts.rs | 2 +- backend/src/main.rs | 85 ++++++---- backend/src/resume_worker.rs | 9 +- backend/src/settings/general.rs | 150 +++++++----------- backend/src/utility.rs | 6 +- package.json | 2 +- 17 files changed, 822 insertions(+), 452 deletions(-) create mode 100644 backend/src/api/async_utils.rs create mode 100644 backend/src/api/handler.rs create mode 100644 backend/src/api_worker.rs diff --git a/backend/Cargo.lock b/backend/Cargo.lock index d6a68ab..0dfa192 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -38,6 +38,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "async-recursion" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -567,12 +589,14 @@ dependencies = [ [[package]] name = "powertools-rs" -version = "1.0.5" +version = "1.1.0" dependencies = [ + "async-trait", "log", "serde", "serde_json", "simplelog", + "tokio", "usdpl-back", ] @@ -1053,12 +1077,15 @@ dependencies = [ [[package]] name = "usdpl-back" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbbc0781e83ba990f8239142e33173a2d2548701775f3db66702d1af4fd0319a" +checksum = "4ca96dac4ee471e9534940f99cb36f5212cbfaf4e7779eb3ba970d3c511d9583" dependencies = [ + "async-recursion", + "async-trait", "bytes", "hex", + "log", "obfstr", "tokio", "usdpl-core", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 9178f52..9fbf9bd 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,15 +1,19 @@ [package] name = "powertools-rs" -version = "1.0.5" +version = "1.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -usdpl-back = { version = "0.6.0", features = ["blocking"]} +usdpl-back = { version = "0.7.0", features = ["blocking"]} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +# async +tokio = { version = "*", features = ["time"] } +async-trait = { version = "0.1" } + # logging log = "0.4" simplelog = "0.12" diff --git a/backend/src/api/async_utils.rs b/backend/src/api/async_utils.rs new file mode 100644 index 0000000..689ac9b --- /dev/null +++ b/backend/src/api/async_utils.rs @@ -0,0 +1,65 @@ +//use usdpl_back::core::serdes::Primitive; +use usdpl_back::AsyncCallable; + +/*pub struct AsyncIsh Result) + Send + Sync, + SG: (Fn(T) -> T) + Send + Sync + 'static, + TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> { + pub trans_setter: TS, // assumed to be pretty fast + pub set_get: SG, // probably has locks (i.e. slow) + pub trans_getter: TG, // assumed to be pretty fast +} + +#[async_trait::async_trait] +impl Result) + Send + Sync, + SG: (Fn(T) -> T) + Send + Sync + 'static, + TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> + AsyncCallable for AsyncIsh { + async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType { + let t_to_set = match (self.trans_setter)(params) { + Ok(t) => t, + Err(e) => return vec![e.into()] + }; + let t_got = match tokio::task::spawn_blocking(|| (self.set_get)(t_to_set)).await { + Ok(t) => t, + Err(e) => return vec![e.to_string().into()], + }; + (self.trans_getter)(t_got) + } +}*/ + +pub struct AsyncIshGetter G) + Send + Sync, + G: (Fn() -> T) + Send + Sync + 'static, + TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> { + pub set_get: Gen, // probably has locks (i.e. slow) + pub trans_getter: TG, // assumed to be pretty fast +} + +#[async_trait::async_trait] +impl G) + Send + Sync, + G: (Fn() -> T) + Send + Sync + 'static, + TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> + AsyncCallable for AsyncIshGetter { + async fn call(&self, _params: super::ApiParameterType) -> super::ApiParameterType { + let getter = (self.set_get)(); + let t_got = match tokio::task::spawn_blocking(move || getter()).await { + Ok(t) => t, + Err(e) => return vec![e.to_string().into()], + }; + (self.trans_getter)(t_got) + } +} + +pub struct Blocking super::ApiParameterType) + Send + Sync> { + pub func: F, +} + +#[async_trait::async_trait] +impl super::ApiParameterType) + Send + Sync> AsyncCallable for Blocking { + async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType { + (self.func)(params) + } +} diff --git a/backend/src/api/battery.rs b/backend/src/api/battery.rs index 6140902..83af197 100644 --- a/backend/src/api/battery.rs +++ b/backend/src/api/battery.rs @@ -1,8 +1,8 @@ -use std::sync::{mpsc::Sender, Arc, Mutex}; +use std::sync::mpsc::{Sender, self}; +use std::sync::Mutex; use usdpl_back::core::serdes::Primitive; -use crate::settings::{Battery, OnSet}; -use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use super::handler::{ApiMessage, BatteryMessage}; /// Current current (ha!) web method pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType { @@ -26,22 +26,18 @@ pub fn charge_design(_: super::ApiParameterType) -> super::ApiParameterType { /// Generate set battery charge rate web method pub fn set_charge_rate( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |rate: f64| + sender.lock() + .unwrap() + .send(ApiMessage::Battery(BatteryMessage::SetChargeRate(Some(rate as u64)))) + .expect("set_charge_rate send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(new_val)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "battery"); - settings_lock.charge_rate = Some(*new_val as _); - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result( - settings_lock.on_set(), - settings_lock.charge_rate.unwrap(), - ) + if let Some(&Primitive::F64(new_val)) = params_in.get(0) { + setter(new_val); + vec![(new_val).into()] } else { vec!["set_charge_rate missing parameter".into()] } @@ -50,30 +46,28 @@ pub fn set_charge_rate( /// Generate get battery charge rate web method pub fn get_charge_rate( - settings: Arc>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let getter = move || { + let (tx, rx) = mpsc::channel(); + let callback = move |rate: Option| tx.send(rate).expect("get_charge_rate callback send failed"); + sender.lock().unwrap().send(ApiMessage::Battery(BatteryMessage::GetChargeRate(Box::new(callback)))).expect("get_charge_rate send failed"); + rx.recv().expect("get_charge_rate callback recv failed") + }; move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "battery"); - vec![settings_lock - .charge_rate - .map(|x| x.into()) - .unwrap_or(Primitive::Empty)] + vec![getter().map(|x| x.into()).unwrap_or(Primitive::Empty)] } } /// Generate unset battery charge rate web method pub fn unset_charge_rate( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety - move |_: super::ApiParameterType| { - let mut settings_lock = unwrap_lock(settings.lock(), "battery"); - settings_lock.charge_rate = None; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result(settings_lock.on_set(), true) + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || sender.lock().unwrap().send(ApiMessage::Battery(BatteryMessage::SetChargeRate(None))).expect("unset_charge_rate send failed"); + move |_params_in: super::ApiParameterType| { + setter(); + vec![true.into()] } } diff --git a/backend/src/api/cpu.rs b/backend/src/api/cpu.rs index 47a9243..0e55c4c 100644 --- a/backend/src/api/cpu.rs +++ b/backend/src/api/cpu.rs @@ -1,8 +1,11 @@ -use std::sync::{mpsc::Sender, Arc, Mutex}; +use std::sync::mpsc::{Sender, self}; +use std::sync::{Arc, Mutex}; use usdpl_back::core::serdes::Primitive; +use usdpl_back::AsyncCallable; -use crate::settings::{Cpu, OnSet, SettingError, SettingVariant, MinMax}; -use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use crate::settings::{Cpu, SettingError, SettingVariant, MinMax}; +//use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use super::handler::{ApiMessage, CpuMessage}; /// Available CPUs web method pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType { @@ -20,29 +23,21 @@ pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType { /// Generate set CPU online web method pub fn set_cpu_online( - settings: Arc>>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |index: usize, value: bool| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetCpuOnline(index, value))).expect("set_cpu_online send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(index)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); - if let Some(cpu) = settings_lock.get_mut(*index as usize) { - if let Some(Primitive::Bool(online)) = params_in.get(1) { - cpu.online = *online; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result( - cpu.on_set(), - cpu.online, - ) - } else { - vec!["set_cpu_online missing parameter 1".into()] - } + if let Some(&Primitive::F64(index)) = params_in.get(0) { + //let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); + if let Some(&Primitive::Bool(online)) = params_in.get(1) { + setter(index as usize, online); + vec![online.into()] } else { - vec!["set_cpu_online cpu index out of bounds".into()] + vec!["set_cpu_online missing parameter 1".into()] } } else { vec!["set_cpu_online missing parameter 0".into()] @@ -51,81 +46,94 @@ pub fn set_cpu_online( } pub fn set_cpus_online( - settings: Arc>>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |values: Vec| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetCpusOnline(values))).expect("set_cpus_online send failed"); move |params_in: super::ApiParameterType| { let mut result = Vec::with_capacity(params_in.len()); - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); + let mut values = Vec::with_capacity(params_in.len()); for i in 0..params_in.len() { if let Primitive::Bool(online) = params_in[i] { - if let Some(cpu) = settings_lock.get_mut(i) { - cpu.online = online; - match cpu.on_set() { - Ok(_) => result.push(cpu.online.into()), - Err(e) => result.push(e.msg.into()) - } - } + values.push(online); + result.push(online.into()); } else { + values.push(true); result.push(format!("Invalid parameter {}", i).into()) } } - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); + setter(values); result } } -pub fn get_cpus_online( - settings: Arc>>, +/*pub fn get_cpus_online( + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let getter = move || { + let (tx, rx) = mpsc::channel(); + let callback = move |values: Vec| tx.send(values).expect("get_cpus_online callback send failed"); + sender.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::GetCpusOnline(Box::new(callback)))).expect("get_cpus_online send failed"); + rx.recv().expect("get_cpus_online callback recv failed") + }; move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "cpu"); - let mut output = Vec::with_capacity(settings_lock.len()); - for cpu in settings_lock.as_slice() { - output.push(cpu.online.into()); + let result = getter(); + let mut output = Vec::with_capacity(result.len()); + for &status in result.as_slice() { + output.push(status.into()); } output } +}*/ + +pub fn get_cpus_online( + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |values: Vec| tx.send(values).expect("get_cpus_online callback send failed"); + sender2.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::GetCpusOnline(Box::new(callback)))).expect("get_cpus_online send failed"); + rx.recv().expect("get_cpus_online callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |result| { + let mut output = Vec::with_capacity(result.len()); + for &status in result.as_slice() { + output.push(status.into()); + } + output + } + } } pub fn set_clock_limits( - settings: Arc>>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |index: usize, value: MinMax| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetClockLimits(index, Some(value)))).expect("set_clock_limits send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(index)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); - if let Some(cpu) = settings_lock.get_mut(*index as usize) { - if let Some(Primitive::F64(min)) = params_in.get(1) { - if let Some(Primitive::F64(max)) = params_in.get(2) { - cpu.clock_limits = Some(MinMax { - min: *min as _, - max: *max as _, - }); - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - match cpu.on_set() { - Ok(_) => vec![ - cpu.clock_limits.as_ref().unwrap().min.into(), - cpu.clock_limits.as_ref().unwrap().max.into(), - ], - Err(e) => vec![e.msg.into()] - } - } else { - vec!["set_clock_limits missing parameter 2".into()] - } + if let Some(&Primitive::F64(index)) = params_in.get(0) { + if let Some(&Primitive::F64(min)) = params_in.get(1) { + if let Some(&Primitive::F64(max)) = params_in.get(2) { + setter(index as usize, MinMax {min: min as u64, max: max as u64}); + vec![min.into(), max.into()] } else { - vec!["set_clock_limits missing parameter 1".into()] + vec!["set_clock_limits missing parameter 2".into()] } } else { - vec!["set_clock_limits cpu index out of bounds".into()] + vec!["set_clock_limits missing parameter 1".into()] } } else { vec!["set_clock_limits missing parameter 0".into()] @@ -134,19 +142,21 @@ pub fn set_clock_limits( } pub fn get_clock_limits( - settings: Arc>>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let getter = move |index: usize| { + let (tx, rx) = mpsc::channel(); + let callback = move |values: Option>| tx.send(values).expect("get_clock_limits callback send failed"); + sender.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::GetClockLimits(index, Box::new(callback)))).expect("get_clock_limits send failed"); + rx.recv().expect("get_clock_limits callback recv failed") + }; move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(index)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); - if let Some(cpu) = settings_lock.get_mut(*index as usize) { - if let Some(min_max) = &cpu.clock_limits { - vec![min_max.min.into(), min_max.max.into()] - } else { - vec![Primitive::Empty, Primitive::Empty] - } + if let Some(&Primitive::F64(index)) = params_in.get(0) { + if let Some(min_max) = getter(index as usize) { + vec![min_max.min.into(), min_max.max.into()] } else { - vec!["get_clock_limits cpu index out of bounds".into()] + vec![Primitive::Empty, Primitive::Empty] } } else { vec!["get_clock_limits missing parameter 0".into()] @@ -155,23 +165,17 @@ pub fn get_clock_limits( } pub fn unset_clock_limits( - settings: Arc>>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |index: usize| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetClockLimits(index, None))).expect("unset_clock_limits send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(index)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); - if let Some(cpu) = settings_lock.get_mut(*index as usize) { - cpu.clock_limits = None; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result(cpu.on_set(), true) - } else { - vec!["get_clock_limits cpu index out of bounds".into()] - } + if let Some(&Primitive::F64(index)) = params_in.get(0) { + setter(index as usize); + vec![true.into()] } else { vec!["get_clock_limits missing parameter 0".into()] } @@ -179,29 +183,20 @@ pub fn unset_clock_limits( } pub fn set_cpu_governor( - settings: Arc>>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |index: usize, governor: String| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetCpuGovernor(index, governor))).expect("set_cpu_governor send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(index)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "cpu"); - if let Some(cpu) = settings_lock.get_mut(*index as usize) { - if let Some(Primitive::String(governor)) = params_in.get(1) { - cpu.governor = governor.to_owned(); - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result( - cpu.on_set(), - &cpu.governor as &str, - ) - } else { - vec!["set_cpu_governor missing parameter 1".into()] - } + if let Some(&Primitive::F64(index)) = params_in.get(0) { + if let Some(Primitive::String(governor)) = params_in.get(1) { + setter(index as usize, governor.to_owned()); + vec![(governor as &str).into()] } else { - vec!["set_cpu_governor cpu index out of bounds".into()] + vec!["set_cpu_governor missing parameter 1".into()] } } else { vec!["set_cpu_governor missing parameter 0".into()] @@ -209,14 +204,46 @@ pub fn set_cpu_governor( } } -pub fn get_cpu_governors( - settings: Arc>>, +pub fn set_cpus_governors( + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |governors: Vec| + sender.lock() + .unwrap() + .send(ApiMessage::Cpu(CpuMessage::SetCpusGovernor(governors))).expect("set_cpus_governor send failed"); + move |params_in: super::ApiParameterType| { + let mut result = Vec::with_capacity(params_in.len()); + let mut values = Vec::with_capacity(params_in.len()); + for i in 0..params_in.len() { + if let Primitive::String(gov) = ¶ms_in[i] { + values.push(gov.to_owned()); + result.push((gov as &str).into()); + } else { + //values.push(true); + result.push(format!("Invalid parameter {}", i).into()) + } + } + setter(values); + result + } +} + +pub fn get_cpu_governors( + sender: Sender, +) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let getter = move || { + let (tx, rx) = mpsc::channel(); + let callback = move |values: Vec| tx.send(values).expect("get_cpu_governors callback send failed"); + sender.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::GetCpusGovernor(Box::new(callback)))).expect("get_cpu_governors send failed"); + rx.recv().expect("get_cpu_governors callback recv failed") + }; move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "cpu"); - let mut output = Vec::with_capacity(settings_lock.len()); - for cpu in settings_lock.as_slice() { - output.push(cpu.governor.clone().into()); + let result = getter(); + let mut output = Vec::with_capacity(result.len()); + for cpu in result.as_slice() { + output.push(cpu.clone().into()); } output } diff --git a/backend/src/api/general.rs b/backend/src/api/general.rs index 4da5021..a0216eb 100644 --- a/backend/src/api/general.rs +++ b/backend/src/api/general.rs @@ -1,29 +1,25 @@ -use std::sync::{mpsc::Sender, Arc, Mutex}; +use std::sync::mpsc::{Sender, self}; +use std::sync::{Arc, Mutex}; use usdpl_back::core::serdes::Primitive; +use usdpl_back::AsyncCallable; -use crate::settings::{General, Settings, OnSet}; -use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +//use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use super::handler::{ApiMessage, GeneralMessage}; /// Generate set persistent web method pub fn set_persistent( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |pers: bool| + sender.lock() + .unwrap() + .send(ApiMessage::General(GeneralMessage::SetPersistent(pers))).expect("set_persistent send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::Bool(new_val)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "general"); - settings_lock.persistent = *new_val; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - let result = super::utility::map_empty_result( - settings_lock.on_set(), - settings_lock.persistent, - ); - log::debug!("Persistent is now {}", settings_lock.persistent); - result + if let Some(&Primitive::Bool(new_val)) = params_in.get(0) { + setter(new_val); + //log::debug!("Persistent is now {}", settings_lock.persistent); + vec![new_val.into()] } else { vec!["set_persistent missing parameter".into()] } @@ -32,30 +28,34 @@ pub fn set_persistent( /// Generate get persistent save mode web method pub fn get_persistent( - settings: Arc>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let getter = move || { + let (tx, rx) = mpsc::channel(); + let callback = move |value: bool| tx.send(value).expect("get_persistent callback send failed"); + sender.lock().unwrap().send(ApiMessage::General(GeneralMessage::GetPersistent(Box::new(callback)))).expect("get_persistent send failed"); + rx.recv().expect("get_persistent callback recv failed") + }; move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "general"); - vec![settings_lock - .persistent.into()] + vec![getter().into()] } } /// Generate load app settings from file web method pub fn load_settings( - settings: Settings, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |path: String, name: String| + sender.lock() + .unwrap() + .send(ApiMessage::LoadSettings(path, name)).expect("load_settings send failed"); move |params_in: super::ApiParameterType| { if let Some(Primitive::String(path)) = params_in.get(0) { if let Some(Primitive::String(name)) = params_in.get(1) { - match settings.load_file(path.into(), name.to_owned(), false) { - Err(e) => vec![e.msg.into()], - Ok(success) => - super::utility::map_empty_result( - settings.clone().on_set(), - success - ) - } + setter(path.to_owned(), name.to_owned()); + vec![true.into()] } else { vec!["load_settings missing name parameter".into()] } @@ -68,10 +68,17 @@ pub fn load_settings( /// Generate load default settings from file web method pub fn load_default_settings( - settings: Settings, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || + sender.lock() + .unwrap() + .send(ApiMessage::LoadMainSettings).expect("load_default_settings send failed"); move |_: super::ApiParameterType| { - match settings.load_file( + setter(); + vec![true.into()] + /*match settings.load_file( crate::consts::DEFAULT_SETTINGS_FILE.into(), crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), true @@ -81,32 +88,76 @@ pub fn load_default_settings( settings.clone().on_set(), success ) - } + }*/ + } +} + +/// Generate load system default settings from file web method +pub fn load_system_settings( + sender: Sender, +) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || + sender.lock() + .unwrap() + .send(ApiMessage::LoadSystemSettings).expect("load_default_settings send failed"); + move |_: super::ApiParameterType| { + setter(); + vec![true.into()] + /*match settings.load_file( + crate::consts::DEFAULT_SETTINGS_FILE.into(), + crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), + true + ) { + Err(e) => vec![e.msg.into()], + Ok(success) => super::utility::map_empty_result( + settings.clone().on_set(), + success + ) + }*/ } } /// Generate get current settings name pub fn get_name( - settings: Arc>, -) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "general"); - vec![settings_lock - .name - .clone() - .into()] + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |name: String| tx.send(name).expect("get_name callback send failed"); + sender2.lock().unwrap().send(ApiMessage::General(GeneralMessage::GetCurrentProfileName(Box::new(callback)))).expect("get_name send failed"); + rx.recv().expect("get_name callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |result| { + vec![result.into()] + } } } /// Generate wait for all locks to be available web method pub fn lock_unlock_all( - settings: Settings, -) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - move |_: super::ApiParameterType| { - let _lock = unwrap_lock(settings.general.lock(), "general"); - let _lock = unwrap_lock(settings.cpus.lock(), "cpus"); - let _lock = unwrap_lock(settings.gpu.lock(), "gpu"); - let _lock = unwrap_lock(settings.battery.lock(), "battery"); - vec![true.into()] + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |x| tx.send(x).expect("lock_unlock_all callback send failed"); + sender2.lock().unwrap().send(ApiMessage::WaitForEmptyQueue(Box::new(callback))).expect("lock_unlock_all send failed"); + rx.recv().expect("lock_unlock_all callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |_| { + vec![true.into()] + } } } diff --git a/backend/src/api/gpu.rs b/backend/src/api/gpu.rs index 5ff5fd3..e23ba85 100644 --- a/backend/src/api/gpu.rs +++ b/backend/src/api/gpu.rs @@ -1,31 +1,25 @@ -use std::sync::{mpsc::Sender, Arc, Mutex}; +use std::sync::mpsc::{Sender, self}; +use std::sync::{Mutex, Arc}; use usdpl_back::core::serdes::Primitive; +use usdpl_back::AsyncCallable; -use crate::settings::{Gpu, OnSet, MinMax}; -use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use crate::settings::MinMax; +//use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use super::handler::{ApiMessage, GpuMessage}; pub fn set_ppt( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |fast: u64, slow: u64| + sender.lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetPpt(Some(fast), Some(slow)))).expect("set_ppt send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(fast_ppt)) = params_in.get(0) { - if let Some(Primitive::F64(slow_ppt)) = params_in.get(1) { - let mut settings_lock = unwrap_lock(settings.lock(), "gpu"); - settings_lock.fast_ppt = Some(*fast_ppt as u64); - settings_lock.slow_ppt = Some(*slow_ppt as u64); - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - match settings_lock.on_set() { - Ok(_) => vec![ - settings_lock.fast_ppt.unwrap().into(), - settings_lock.slow_ppt.unwrap().into() - ], - Err(e) => vec![e.msg.into()], - } + if let Some(&Primitive::F64(fast_ppt)) = params_in.get(0) { + if let Some(&Primitive::F64(slow_ppt)) = params_in.get(1) { + setter(fast_ppt as u64, slow_ppt as u64); + vec![(fast_ppt as u64).into(), (slow_ppt as u64).into()] } else { vec!["set_ppt missing parameter 1".into()] } @@ -36,60 +30,59 @@ pub fn set_ppt( } pub fn get_ppt( - settings: Arc>, -) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "gpu"); - let fast_ppt = settings_lock.fast_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty); - let slow_ppt = settings_lock.slow_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty); - vec![fast_ppt, slow_ppt] + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |ppt: (Option, Option)| tx.send(ppt).expect("get_ppt callback send failed"); + sender2.lock().unwrap().send(ApiMessage::Gpu(GpuMessage::GetPpt(Box::new(callback)))).expect("get_ppt send failed"); + rx.recv().expect("get_ppt callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |(fast, slow): (Option, Option)| { + vec![ + fast.map(|x| x.into()).unwrap_or(Primitive::Empty), + slow.map(|x| x.into()).unwrap_or(Primitive::Empty), + ] + } } } pub fn unset_ppt( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || + sender.lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetPpt(None, None))).expect("set_ppt send failed"); move |_: super::ApiParameterType| { - let mut settings_lock = unwrap_lock(settings.lock(), "gpu"); - settings_lock.fast_ppt = None; - settings_lock.slow_ppt = None; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result( - settings_lock.on_set(), - Primitive::Empty, - ) + setter(); + vec![true.into()] } } pub fn set_clock_limits( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |value: MinMax| + sender.lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetClockLimits(Some(value)))).expect("set_clock_limits send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::F64(min)) = params_in.get(0) { - if let Some(Primitive::F64(max)) = params_in.get(1) { - let mut settings_lock = unwrap_lock(settings.lock(), "gpu"); - settings_lock.clock_limits = Some(MinMax { - min: *min as _, - max: *max as _, + if let Some(&Primitive::F64(min)) = params_in.get(0) { + if let Some(&Primitive::F64(max)) = params_in.get(1) { + setter(MinMax { + min: min as _, + max: max as _, }); - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - match settings_lock.on_set() { - Ok(_) => vec![ - settings_lock.clock_limits.as_ref().unwrap().min.into(), - settings_lock.clock_limits.as_ref().unwrap().max.into(), - ], - Err(e) => vec![e.msg.into()] - } + vec![(min as u64).into(), (max as u64).into()] } else { vec!["set_clock_limits missing parameter 1".into()] } @@ -100,51 +93,54 @@ pub fn set_clock_limits( } pub fn get_clock_limits( - settings: Arc>, -) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "gpu"); - if let Some(min_max) = &settings_lock.clock_limits { - vec![min_max.min.into(), min_max.max.into()] - } else { - vec![Primitive::Empty, Primitive::Empty] + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move|| { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |clocks: Option>| tx.send(clocks).expect("get_clock_limits callback send failed"); + sender2.lock().unwrap().send(ApiMessage::Gpu(GpuMessage::GetClockLimits(Box::new(callback)))).expect("get_clock_limits send failed"); + rx.recv().expect("get_clock_limits callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |clocks: Option>| { + clocks.map(|x| vec![ + x.min.into(), x.max.into() + ]).unwrap_or_else(|| vec![Primitive::Empty, Primitive::Empty]) } } } pub fn unset_clock_limits( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || + sender.lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetClockLimits(None))).expect("unset_clock_limits send failed"); move |_: super::ApiParameterType| { - let mut settings_lock = unwrap_lock(settings.lock(), "gpu"); - settings_lock.clock_limits = None; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result(settings_lock.on_set(), true) + setter(); + vec![true.into()] } } pub fn set_slow_memory( - settings: Arc>, - saver: Sender<()>, + sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move |value: bool| + sender.lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetSlowMemory(value))).expect("unset_clock_limits send failed"); move |params_in: super::ApiParameterType| { - if let Some(Primitive::Bool(memory_is_slow)) = params_in.get(0) { - let mut settings_lock = unwrap_lock(settings.lock(), "gpu"); - settings_lock.slow_memory = *memory_is_slow; - unwrap_maybe_fatal( - unwrap_lock(saver.lock(), "save channel").send(()), - "Failed to send on save channel", - ); - super::utility::map_empty_result( - settings_lock.on_set(), - settings_lock.slow_memory, - ) + if let Some(&Primitive::Bool(memory_is_slow)) = params_in.get(0) { + setter(memory_is_slow); + vec![memory_is_slow.into()] } else { vec!["set_slow_memory missing parameter 0".into()] } @@ -152,10 +148,22 @@ pub fn set_slow_memory( } pub fn get_slow_memory( - settings: Arc>, -) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { - move |_: super::ApiParameterType| { - let settings_lock = unwrap_lock(settings.lock(), "cpu"); - vec![settings_lock.slow_memory.into()] + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move || { + let (tx, rx) = mpsc::channel(); + let callback = move |value: bool| tx.send(value).expect("get_slow_memory callback send failed"); + sender2.lock().unwrap().send(ApiMessage::Gpu(GpuMessage::GetSlowMemory(Box::new(callback)))).expect("get_slow_memory send failed"); + rx.recv().expect("get_slow_memory callback recv failed") + } + }; + super::async_utils::AsyncIshGetter { + set_get: getter, + trans_getter: |value: bool| { + vec![value.into()] + } } } diff --git a/backend/src/api/handler.rs b/backend/src/api/handler.rs new file mode 100644 index 0000000..46d6bd2 --- /dev/null +++ b/backend/src/api/handler.rs @@ -0,0 +1,199 @@ +use std::sync::mpsc::{self, Receiver, Sender}; + +use crate::settings::{Settings, Cpu, Gpu, Battery, General, OnSet, OnResume, MinMax}; +use crate::persist::SettingsJson; +use crate::utility::unwrap_maybe_fatal; + +type Callback = Box; + +pub enum ApiMessage { + Battery(BatteryMessage), + Cpu(CpuMessage), + Gpu(GpuMessage), + General(GeneralMessage), + OnResume, + WaitForEmptyQueue(Callback<()>), + LoadSettings(String, String), // (path, name) + LoadMainSettings, + LoadSystemSettings, +} + +pub enum BatteryMessage { + SetChargeRate(Option), + GetChargeRate(Callback>), +} + +impl BatteryMessage { + fn process(self, settings: &mut Battery) { + match self { + Self::SetChargeRate(rate) => settings.charge_rate = rate, + Self::GetChargeRate(cb) => cb(settings.charge_rate), + } + } +} + +pub enum CpuMessage { + SetCpuOnline(usize, bool), + SetCpusOnline(Vec), + GetCpusOnline(Callback>), + SetClockLimits(usize, Option>), + GetClockLimits(usize, Callback>>), + SetCpuGovernor(usize, String), + SetCpusGovernor(Vec), + GetCpusGovernor(Callback>), +} + +impl CpuMessage { + fn process(self, settings: &mut Vec) { + match self { + Self::SetCpuOnline(index, status) => {settings.get_mut(index).map(|c| c.online = status);}, + Self::SetCpusOnline(cpus) => { + for i in 0..cpus.len() { + settings.get_mut(i).map(|c| c.online = cpus[i]); + } + }, + Self::GetCpusOnline(cb) => { + let mut result = Vec::with_capacity(settings.len()); + for cpu in settings { + result.push(cpu.online); + } + cb(result); + }, + Self::SetClockLimits(index, clocks) => {settings.get_mut(index).map(|c| c.clock_limits = clocks);}, + Self::GetClockLimits(index, cb) => {settings.get(index).map(|c| cb(c.clock_limits.clone()));}, + Self::SetCpuGovernor(index, gov) => {settings.get_mut(index).map(|c| c.governor = gov);}, + Self::SetCpusGovernor(govs) => { + for i in 0..govs.len() { + settings.get_mut(i).map(|c| c.governor = govs[i].clone()); + } + }, + Self::GetCpusGovernor(cb) => { + let mut result = Vec::with_capacity(settings.len()); + for cpu in settings { + result.push(cpu.governor.clone()); + } + cb(result); + } + } + } +} + +pub enum GpuMessage { + SetPpt(Option, Option), // (fast, slow) + GetPpt(Callback<(Option, Option)>), + SetClockLimits(Option>), + GetClockLimits(Callback>>), + SetSlowMemory(bool), + GetSlowMemory(Callback), +} + +impl GpuMessage { + fn process(self, settings: &mut Gpu) { + match self { + Self::SetPpt(fast, slow) => { + settings.fast_ppt = fast; + settings.slow_ppt = slow; + }, + Self::GetPpt(cb) => cb((settings.fast_ppt, settings.slow_ppt)), + Self::SetClockLimits(clocks) => settings.clock_limits = clocks, + Self::GetClockLimits(cb) => cb(settings.clock_limits.clone()), + Self::SetSlowMemory(val) => settings.slow_memory = val, + Self::GetSlowMemory(cb) => cb(settings.slow_memory), + } + } +} + +pub enum GeneralMessage { + SetPersistent(bool), + GetPersistent(Callback), + GetCurrentProfileName(Callback), +} + +impl GeneralMessage { + fn process(self, settings: &mut General) { + match self { + Self::SetPersistent(val) => settings.persistent = val, + Self::GetPersistent(cb) => cb(settings.persistent), + Self::GetCurrentProfileName(cb) => cb(settings.name.clone()), + } + } +} + +pub struct ApiMessageHandler { + intake: Receiver, + on_empty: Vec>, +} + +impl ApiMessageHandler { + pub fn process_forever(&mut self, settings: &mut Settings) { + while let Ok(msg) = self.intake.recv() { + self.process(settings, msg); + while let Ok(msg) = self.intake.try_recv() { + self.process(settings, msg); + } + // run on_set + if let Err(e) = settings.on_set() { + log::error!("Settings on_set() err: {}", e); + } + // do callbacks + for func in self.on_empty.drain(..) { + func(()); + } + // save + log::debug!("api_worker is saving..."); + let is_persistent = settings.general.persistent; + if is_persistent { + let save_path = crate::utility::settings_dir() + .join(settings.general.path.clone()); + let settings_clone = settings.clone(); + let save_json: SettingsJson = settings_clone.into(); + unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings"); + log::debug!("Saved settings to {}", save_path.display()); + } else { + log::debug!("Ignored save request for non-persistent settings"); + } + } + } + + pub fn process(&mut self, settings: &mut Settings, message: ApiMessage) { + match message { + ApiMessage::Battery(x) => x.process(&mut settings.battery), + ApiMessage::Cpu(x) => x.process(&mut settings.cpus), + ApiMessage::Gpu(x) => x.process(&mut settings.gpu), + ApiMessage::General(x) => x.process(&mut settings.general), + ApiMessage::OnResume => { + if let Err(e) = settings.on_resume() { + log::error!("Settings on_resume() err: {}", e); + } + } + ApiMessage::WaitForEmptyQueue(callback) => self.on_empty.push(callback), + ApiMessage::LoadSettings(path, name) => { + match settings.load_file(path.into(), name, false) { + Ok(success) => log::info!("Loaded settings file? {}", success), + Err(e) => log::warn!("Load file err: {}", e), + } + } + ApiMessage::LoadMainSettings => { + match settings.load_file( + crate::consts::DEFAULT_SETTINGS_FILE.into(), + crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), + true + ) { + Ok(success) => log::info!("Loaded main settings file? {}", success), + Err(e) => log::warn!("Load file err: {}", e), + } + } + ApiMessage::LoadSystemSettings => { + settings.load_system_default(); + } + } + } + + pub fn new() -> (Self, Sender) { + let (tx, rx) = mpsc::channel(); + (Self { + intake: rx, + on_empty: Vec::with_capacity(4), + }, tx) + } +} diff --git a/backend/src/api/mod.rs b/backend/src/api/mod.rs index 75e2a6d..03412a8 100644 --- a/backend/src/api/mod.rs +++ b/backend/src/api/mod.rs @@ -2,6 +2,8 @@ pub mod battery; pub mod cpu; pub mod general; pub mod gpu; +pub mod handler; +mod async_utils; mod utility; pub(super) type ApiParameterType = Vec; diff --git a/backend/src/api/utility.rs b/backend/src/api/utility.rs index c5b4bc0..c797f60 100644 --- a/backend/src/api/utility.rs +++ b/backend/src/api/utility.rs @@ -14,7 +14,7 @@ pub fn map_result>(result: Result) -> super: } } -#[inline] +/*#[inline] pub fn map_empty_result>( result: Result<(), SettingError>, success: T, @@ -26,4 +26,4 @@ pub fn map_empty_result>( vec![e.msg.into()] }, } -} +}*/ diff --git a/backend/src/api_worker.rs b/backend/src/api_worker.rs new file mode 100644 index 0000000..2ddff02 --- /dev/null +++ b/backend/src/api_worker.rs @@ -0,0 +1,13 @@ +use std::thread::{self, JoinHandle}; + +use crate::settings::Settings; +//use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; +use crate::api::handler::ApiMessageHandler; + +pub fn spawn(mut settings: Settings, mut handler: ApiMessageHandler) -> JoinHandle<()> { + thread::spawn(move || { + log::info!("api_worker starting..."); + handler.process_forever(&mut settings); + log::warn!("api_worker completed!"); + }) +} diff --git a/backend/src/consts.rs b/backend/src/consts.rs index 43e23b7..055d0ff 100644 --- a/backend/src/consts.rs +++ b/backend/src/consts.rs @@ -4,4 +4,4 @@ pub const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME"); pub const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION"); pub const DEFAULT_SETTINGS_FILE: &str = "default_settings.json"; -pub const DEFAULT_SETTINGS_NAME: &str = "Default"; +pub const DEFAULT_SETTINGS_NAME: &str = "Main"; diff --git a/backend/src/main.rs b/backend/src/main.rs index 66ef8e2..ed9586f 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,7 +6,8 @@ mod state; mod consts; use consts::*; mod resume_worker; -mod save_worker; +//mod save_worker; +mod api_worker; mod utility; use settings::OnSet; @@ -49,14 +50,16 @@ fn main() -> Result<(), ()> { log::debug!("Settings: {:?}", loaded_settings); - let (_save_handle, save_sender) = save_worker::spawn(loaded_settings.clone()); - let _resume_handle = resume_worker::spawn(loaded_settings.clone()); + let (api_handler, api_sender) = crate::api::handler::ApiMessageHandler::new(); + + //let (_save_handle, save_sender) = save_worker::spawn(loaded_settings.clone()); + let _resume_handle = resume_worker::spawn(api_sender.clone()); if let Err(e) = loaded_settings.on_set() { log::error!("Startup Settings.on_set() error: {}", e); } - Instance::new(PORT) + let instance = Instance::new(PORT) .register("V_INFO", |_: Vec| { vec![format!("{} v{}", PACKAGE_NAME, PACKAGE_VERSION).into()] }) @@ -67,107 +70,119 @@ fn main() -> Result<(), ()> { .register("BATTERY_charge_design", api::battery::charge_design) .register( "BATTERY_set_charge_rate", - api::battery::set_charge_rate(loaded_settings.battery.clone(), save_sender.clone()), + api::battery::set_charge_rate(api_sender.clone()), ) .register( "BATTERY_get_charge_rate", - api::battery::get_charge_rate(loaded_settings.battery.clone()), + api::battery::get_charge_rate(api_sender.clone()), ) .register( "BATTERY_unset_charge_rate", - api::battery::unset_charge_rate(loaded_settings.battery.clone(), save_sender.clone()), + api::battery::unset_charge_rate(api_sender.clone()), ) // cpu API functions .register("CPU_count", api::cpu::max_cpus) .register( "CPU_set_online", - api::cpu::set_cpu_online(loaded_settings.cpus.clone(), save_sender.clone()) + api::cpu::set_cpu_online(api_sender.clone()) ) .register( "CPU_set_onlines", - api::cpu::set_cpus_online(loaded_settings.cpus.clone(), save_sender.clone()) + api::cpu::set_cpus_online(api_sender.clone()) ) - .register( + .register_async( "CPU_get_onlines", - api::cpu::get_cpus_online(loaded_settings.cpus.clone()) + api::cpu::get_cpus_online(api_sender.clone()) ) .register( "CPU_set_clock_limits", - api::cpu::set_clock_limits(loaded_settings.cpus.clone(), save_sender.clone()) + api::cpu::set_clock_limits(api_sender.clone()) ) .register( "CPU_get_clock_limits", - api::cpu::get_clock_limits(loaded_settings.cpus.clone()) + api::cpu::get_clock_limits(api_sender.clone()) ) .register( "CPU_unset_clock_limits", - api::cpu::unset_clock_limits(loaded_settings.cpus.clone(), save_sender.clone()) + api::cpu::unset_clock_limits(api_sender.clone()) ) .register( "CPU_set_governor", - api::cpu::set_cpu_governor(loaded_settings.cpus.clone(), save_sender.clone()) + api::cpu::set_cpu_governor(api_sender.clone()) + ) + .register( + "CPU_set_governors", + api::cpu::set_cpus_governors(api_sender.clone()) ) .register( "CPU_get_governors", - api::cpu::get_cpu_governors(loaded_settings.cpus.clone()) + api::cpu::get_cpu_governors(api_sender.clone()) ) // gpu API functions .register( "GPU_set_ppt", - api::gpu::set_ppt(loaded_settings.gpu.clone(), save_sender.clone()) + api::gpu::set_ppt(api_sender.clone()) ) - .register( + .register_async( "GPU_get_ppt", - api::gpu::get_ppt(loaded_settings.gpu.clone()) + api::gpu::get_ppt(api_sender.clone()) ) .register( "GPU_unset_ppt", - api::gpu::unset_ppt(loaded_settings.gpu.clone(), save_sender.clone()) + api::gpu::unset_ppt(api_sender.clone()) ) .register( "GPU_set_clock_limits", - api::gpu::set_clock_limits(loaded_settings.gpu.clone(), save_sender.clone()) + api::gpu::set_clock_limits(api_sender.clone()) ) - .register( + .register_async( "GPU_get_clock_limits", - api::gpu::get_clock_limits(loaded_settings.gpu.clone()) + api::gpu::get_clock_limits(api_sender.clone()) ) .register( "GPU_unset_clock_limits", - api::gpu::unset_clock_limits(loaded_settings.gpu.clone(), save_sender.clone()) + api::gpu::unset_clock_limits(api_sender.clone()) ) .register( "GPU_set_slow_memory", - api::gpu::set_slow_memory(loaded_settings.gpu.clone(), save_sender.clone()) + api::gpu::set_slow_memory(api_sender.clone()) ) - .register( + .register_async( "GPU_get_slow_memory", - api::gpu::get_slow_memory(loaded_settings.gpu.clone()) + api::gpu::get_slow_memory(api_sender.clone()) ) // general API functions .register( "GENERAL_set_persistent", - api::general::set_persistent(loaded_settings.general.clone(), save_sender.clone()) + api::general::set_persistent(api_sender.clone()) ) .register( "GENERAL_get_persistent", - api::general::get_persistent(loaded_settings.general.clone()) + api::general::get_persistent(api_sender.clone()) ) .register( "GENERAL_load_settings", - api::general::load_settings(loaded_settings.clone()) + api::general::load_settings(api_sender.clone()) ) .register( "GENERAL_load_default_settings", - api::general::load_default_settings(loaded_settings.clone()) + api::general::load_default_settings(api_sender.clone()) ) .register( + "GENERAL_load_system_settings", + api::general::load_system_settings(api_sender.clone()) + ) + .register_async( "GENERAL_get_name", - api::general::get_name(loaded_settings.general.clone()) + api::general::get_name(api_sender.clone()) ) - .register( + .register_async( "GENERAL_wait_for_unlocks", - api::general::lock_unlock_all(loaded_settings.clone()) - ) + api::general::lock_unlock_all(api_sender.clone()) + ); + + api_worker::spawn(loaded_settings, api_handler); + + instance .run_blocking() } diff --git a/backend/src/resume_worker.rs b/backend/src/resume_worker.rs index 9560d84..2c9279d 100644 --- a/backend/src/resume_worker.rs +++ b/backend/src/resume_worker.rs @@ -1,12 +1,13 @@ use std::thread::{self, JoinHandle}; use std::time::{Duration, Instant}; +use std::sync::mpsc::Sender; -use crate::settings::{OnResume, Settings}; -use crate::utility::unwrap_maybe_fatal; +use crate::api::handler::ApiMessage; +//use crate::utility::unwrap_maybe_fatal; const ALLOWED_ERROR: f64 = 100.0; // period of 10ms with 100x means sleep has to be >= 1s to be detected -pub fn spawn(settings: Settings) -> JoinHandle<()> { +pub fn spawn(sender: Sender) -> JoinHandle<()> { thread::spawn(move || { log::info!("resume_worker starting..."); let duration = Duration::from_millis(10); // very low so it detects before Steam client does @@ -18,7 +19,7 @@ pub fn spawn(settings: Settings) -> JoinHandle<()> { if old_start.as_secs_f64() > duration.as_secs_f64() * (1.0 + ALLOWED_ERROR) { // has just resumed from sleep log::info!("Resume detected"); - unwrap_maybe_fatal(settings.on_resume(), "On resume failure"); + sender.send(ApiMessage::OnResume).expect("resume_worker send failed"); log::debug!( "OnResume completed after sleeping for {}s", old_start.as_secs_f32() diff --git a/backend/src/settings/general.rs b/backend/src/settings/general.rs index 5c40ea4..bb3ff17 100644 --- a/backend/src/settings/general.rs +++ b/backend/src/settings/general.rs @@ -1,11 +1,11 @@ use std::convert::Into; use std::path::PathBuf; -use std::sync::{Arc, Mutex}; +//use std::sync::{Arc, Mutex}; use super::{Battery, Cpu, Gpu}; use super::{OnResume, OnSet, SettingError}; use crate::persist::{CpuJson, SettingsJson}; -use crate::utility::unwrap_lock; +//use crate::utility::unwrap_lock; const LATEST_VERSION: u64 = 0; @@ -43,24 +43,20 @@ impl OnSet for General { #[derive(Debug, Clone)] pub struct Settings { - pub general: Arc>, - pub cpus: Arc>>, - pub gpu: Arc>, - pub battery: Arc>, + pub general: General, + pub cpus: Vec, + pub gpu: Gpu, + pub battery: Battery, } impl OnSet for Settings { fn on_set(&mut self) -> Result<(), SettingError> { - unwrap_lock(self.battery.lock(), "battery").on_set()?; - { - // cpu lock scope - let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu"); - for cpu in cpu_lock.iter_mut() { - cpu.on_set()?; - } + self.battery.on_set()?; + for cpu in self.cpus.iter_mut() { + cpu.on_set()?; } - unwrap_lock(self.gpu.lock(), "gpu").on_set()?; - unwrap_lock(self.general.lock(), "general").on_set()?; + self.gpu.on_set()?; + self.general.on_set()?; Ok(()) } } @@ -70,24 +66,24 @@ impl Settings { pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self { match other.version { 0 => Self { - general: Arc::new(Mutex::new(General { + general: General { persistent: other.persistent, path: json_path, name: other.name, - })), - cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))), - gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))), - battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))), + }, + cpus: Self::convert_cpus(other.cpus, other.version), + gpu: Gpu::from_json(other.gpu, other.version), + battery: Battery::from_json(other.battery, other.version), }, _ => Self { - general: Arc::new(Mutex::new(General { + general: General { persistent: other.persistent, path: json_path, name: other.name, - })), - cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))), - gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))), - battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))), + }, + cpus: Self::convert_cpus(other.cpus, other.version), + gpu: Gpu::from_json(other.gpu, other.version), + battery: Battery::from_json(other.battery, other.version), }, } } @@ -117,35 +113,26 @@ impl Settings { pub fn system_default(json_path: PathBuf) -> Self { Self { - general: Arc::new(Mutex::new(General { + general: General { persistent: false, path: json_path, name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), - })), - cpus: Arc::new(Mutex::new(Cpu::system_default())), - gpu: Arc::new(Mutex::new(Gpu::system_default())), - battery: Arc::new(Mutex::new(Battery::system_default())), + }, + cpus: Cpu::system_default(), + gpu: Gpu::system_default(), + battery: Battery::system_default(), } } - fn load_system_default(&self) { - { - let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu"); - *cpu_lock = Cpu::system_default(); - } - { - let mut gpu_lock = unwrap_lock(self.gpu.lock(), "gpu"); - *gpu_lock = Gpu::system_default(); - } - { - let mut battery_lock = unwrap_lock(self.battery.lock(), "battery"); - *battery_lock = Battery::system_default(); - } + pub fn load_system_default(&mut self) { + self.cpus = Cpu::system_default(); + self.gpu = Gpu::system_default(); + self.battery = Battery::system_default(); } - pub fn load_file(&self, filename: PathBuf, name: String, system_defaults: bool) -> Result { + pub fn load_file(&mut self, filename: PathBuf, name: String, system_defaults: bool) -> Result { let json_path = crate::utility::settings_dir().join(filename); - let mut general_lock = unwrap_lock(self.general.lock(), "general"); + //let mut general_lock = unwrap_lock(self.general.lock(), "general"); if json_path.exists() { let settings_json = SettingsJson::open(&json_path).map_err(|e| SettingError { msg: e.to_string(), @@ -153,53 +140,38 @@ impl Settings { })?; if !settings_json.persistent { log::warn!("Loaded persistent config `{}` ({}) with persistent=false", &settings_json.name, json_path.display()); - general_lock.persistent = false; - general_lock.name = name; + self.general.persistent = false; + self.general.name = name; } else { - let new_cpus = Self::convert_cpus(settings_json.cpus, settings_json.version); - let new_gpu = Gpu::from_json(settings_json.gpu, settings_json.version); - let new_battery = Battery::from_json(settings_json.battery, settings_json.version); - { - let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu"); - *cpu_lock = new_cpus; - } - { - let mut gpu_lock = unwrap_lock(self.gpu.lock(), "gpu"); - *gpu_lock = new_gpu; - } - { - let mut battery_lock = unwrap_lock(self.battery.lock(), "battery"); - *battery_lock = new_battery; - } - general_lock.persistent = true; - general_lock.name = settings_json.name; + self.cpus = Self::convert_cpus(settings_json.cpus, settings_json.version); + self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version); + self.battery = Battery::from_json(settings_json.battery, settings_json.version); + self.general.persistent = true; + self.general.name = settings_json.name; } } else { if system_defaults { self.load_system_default(); } - general_lock.persistent = false; - general_lock.name = name; + self.general.persistent = false; + self.general.name = name; } - general_lock.path = json_path; - Ok(general_lock.persistent) + self.general.path = json_path; + Ok(self.general.persistent) } } impl OnResume for Settings { fn on_resume(&self) -> Result<(), SettingError> { - log::debug!("Locking settings for on_resume"); - unwrap_lock(self.battery.lock(), "battery").on_resume()?; - log::debug!("Got battery lock"); - { - let cpu_lock = unwrap_lock(self.cpus.lock(), "cpu"); - log::debug!("Got cpus lock"); - for cpu in cpu_lock.iter() { - cpu.on_resume()?; - } + log::debug!("Applying settings for on_resume"); + self.battery.on_resume()?; + log::debug!("Resumed battery"); + for cpu in self.cpus.iter() { + cpu.on_resume()?; } - unwrap_lock(self.gpu.lock(), "gpu").on_resume()?; - log::debug!("Got gpu lock"); + log::debug!("Resumed CPUs"); + self.gpu.on_resume()?; + log::debug!("Resumed GPU"); Ok(()) } } @@ -207,26 +179,18 @@ impl OnResume for Settings { impl Into for Settings { #[inline] fn into(self) -> SettingsJson { - log::debug!("Locking settings to convert into json"); - let gen_lock = unwrap_lock(self.general.lock(), "general"); - log::debug!("Got general lock"); - let cpu_lock = unwrap_lock(self.cpus.lock(), "cpu"); - log::debug!("Got cpus lock"); - let gpu_lock = unwrap_lock(self.gpu.lock(), "gpu"); - log::debug!("Got gpu lock"); - let batt_lock = unwrap_lock(self.battery.lock(), "battery"); - log::debug!("Got battery lock"); + log::debug!("Converting into json"); SettingsJson { version: LATEST_VERSION, - name: gen_lock.name.clone(), - persistent: gen_lock.persistent, - cpus: cpu_lock + name: self.general.name.clone(), + persistent: self.general.persistent, + cpus: self.cpus .clone() .drain(..) .map(|cpu| cpu.into()) .collect(), - gpu: gpu_lock.clone().into(), - battery: batt_lock.clone().into(), + gpu: self.gpu.clone().into(), + battery: self.battery.clone().into(), } } } diff --git a/backend/src/utility.rs b/backend/src/utility.rs index 1369816..1c1f90c 100644 --- a/backend/src/utility.rs +++ b/backend/src/utility.rs @@ -1,5 +1,5 @@ use std::fmt::Display; -use std::sync::{LockResult, MutexGuard}; +//use std::sync::{LockResult, MutexGuard}; pub fn unwrap_maybe_fatal(result: Result, message: &str) -> T { match result { @@ -11,7 +11,7 @@ pub fn unwrap_maybe_fatal(result: Result, message: & } } -pub fn unwrap_lock<'a, T: Sized>( +/*pub fn unwrap_lock<'a, T: Sized>( result: LockResult>, lock_name: &str, ) -> MutexGuard<'a, T> { @@ -22,7 +22,7 @@ pub fn unwrap_lock<'a, T: Sized>( panic!("Failed to acquire {} lock: {}", lock_name, e); } } -} +}*/ pub fn settings_dir() -> std::path::PathBuf { usdpl_back::api::dirs::home() diff --git a/package.json b/package.json index c35eabe..e811299 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "PowerTools", - "version": "1.0.5", + "version": "1.1.0", "description": "Power tweaks for power users", "scripts": { "build": "shx rm -rf dist && rollup -c",