Use tokio in asusctl

This commit is contained in:
Luke D. Jones
2022-09-22 22:36:16 +12:00
parent 3b9cf474a7
commit 9608d190b9
15 changed files with 148 additions and 209 deletions

View File

@@ -4,16 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased - 4.4.1]
## [Unreleased - 4.5.0]
### Added
- intofy watches on:
- `charge_control_end_threshold`
- `panel_od`
- `gpu_mux_mode`
- `platform_profile`
- keyboard brightness
- These allow for updating any associated config and sending dbus notifications.
### Changed
- Use loops to ensure that mutex is gained for LED changes.
- asusctl now uses tokio for async runtime. This helps simplify some code.
### Breaking
- DBUS: all charge control methods renamed:
- `ChargeControlEndThreshold`

26
Cargo.lock generated
View File

@@ -588,8 +588,8 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"smol",
"sysfs-class",
"tokio",
"toml",
"zbus",
"zvariant",
@@ -1705,6 +1705,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.7"
@@ -2537,11 +2547,25 @@ dependencies = [
"autocfg",
"libc",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.9"

View File

@@ -25,7 +25,7 @@ rog_profiles = { path = "../rog-profiles" }
rog_dbus = { path = "../rog-dbus" }
async-trait = "^0.1"
smol = "^1.2"
tokio = { version = "^1.21.1", features = ["macros", "rt-multi-thread", "time"]}
# cli and logging
log = "^0.4"

View File

@@ -13,7 +13,6 @@ use rog_anime::{
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
};
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw};
use smol::Executor;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
convert::TryFrom,
@@ -230,11 +229,7 @@ impl CtrlAnimeTask {
#[async_trait]
impl crate::CtrlTask for CtrlAnimeTask {
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
_: SignalContext<'a>,
) -> Result<(), RogError> {
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let run_action =
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
if start {
@@ -251,7 +246,6 @@ impl crate::CtrlTask for CtrlAnimeTask {
let inner3 = self.inner.clone();
let inner4 = self.inner.clone();
self.create_sys_event_tasks(
executor,
// Loop is required to try an attempt to get the mutex *without* blocking
// other threads - it is possible to end up with deadlocks otherwise.
move || loop {

View File

@@ -114,7 +114,7 @@ impl From<&AuraPowerConfig> for AuraPowerDev {
}
#[derive(Deserialize, Serialize)]
#[serde(default)]
// #[serde(default)]
pub struct AuraConfig {
pub brightness: LedBrightness,
pub current_mode: AuraModeNum,
@@ -271,7 +271,7 @@ impl AuraConfig {
if l == 0 {
warn!("File is empty {}", AURA_CONFIG_PATH);
} else {
let x: AuraConfig = serde_json::from_str(&buf)
let x = serde_json::from_str(&buf)
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
*self = x;
}

View File

@@ -11,12 +11,11 @@ use rog_aura::{
};
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
use smol::Executor;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use zbus::SignalContext;
use zbus::{export::futures_util::StreamExt, SignalContext};
use crate::GetSupported;
@@ -95,11 +94,7 @@ impl CtrlKbdLedTask {
#[async_trait]
impl CtrlTask for CtrlKbdLedTask {
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
_: SignalContext<'a>,
) -> Result<(), RogError> {
async fn create_tasks(&self, _: SignalContext<'static>) -> Result<(), RogError> {
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
// If waking up
if !start {
@@ -123,7 +118,6 @@ impl CtrlTask for CtrlKbdLedTask {
let inner3 = self.inner.clone();
let inner4 = self.inner.clone();
self.create_sys_event_tasks(
executor,
// Loop so that we do aquire the lock but also don't block other
// threads (prevents potential deadlocks)
move || loop {
@@ -156,20 +150,18 @@ impl CtrlTask for CtrlKbdLedTask {
let ctrl2 = self.inner.clone();
if let Ok(ctrl) = self.inner.lock() {
let mut watch = ctrl.kd_brightness.monitor_brightness()?;
executor
.spawn(async move {
let mut buffer = [0; 1024];
loop {
if let Ok(events) = watch.read_events_blocking(&mut buffer) {
for _ in events {
if let Ok(lock) = ctrl2.try_lock() {
load_save(true, lock);
}
}
tokio::spawn(async move {
let mut buffer = [0; 32];
watch
.event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
if let Ok(lock) = ctrl2.try_lock() {
load_save(true, lock);
}
}
})
.detach();
})
.await;
});
}
Ok(())

View File

@@ -4,7 +4,6 @@ use async_trait::async_trait;
use log::{info, warn};
use rog_platform::platform::{AsusPlatform, GpuMode};
use rog_platform::supported::RogBiosSupportedFunctions;
use smol::Executor;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
@@ -129,7 +128,7 @@ impl CtrlRogBios {
Ok(())
}
fn set_panel_od(&self, enable: bool) -> Result<(), RogError> {
fn set_panel_overdrive(&self, enable: bool) -> Result<(), RogError> {
self.platform.set_panel_od(enable).map_err(|err| {
warn!("CtrlRogBios: set_panel_overdrive {}", err);
err
@@ -197,7 +196,7 @@ impl CtrlRogBios {
#[dbus_interface(signal)]
async fn notify_post_boot_sound(ctxt: &SignalContext<'_>, on: bool) -> zbus::Result<()> {}
async fn set_panel_overdrive(
async fn set_panel_od(
&mut self,
#[zbus(signal_context)] ctxt: SignalContext<'_>,
overdrive: bool,
@@ -256,7 +255,7 @@ impl crate::Reloadable for CtrlRogBios {
} else {
false
};
self.set_panel_od(p)?;
self.set_panel_overdrive(p)?;
}
Ok(())
}
@@ -269,22 +268,17 @@ impl CtrlRogBios {
#[async_trait]
impl CtrlTask for CtrlRogBios {
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
signal_ctxt: SignalContext<'a>,
) -> Result<(), RogError> {
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let platform1 = self.clone();
let platform2 = self.clone();
self.create_sys_event_tasks(
executor,
move || {},
move || {
info!("CtrlRogBios reloading panel_od");
if let Ok(lock) = platform1.config.try_lock() {
if platform1.platform.has_panel_od() {
platform1
.set_panel_od(lock.panel_od)
.set_panel_overdrive(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
@@ -299,7 +293,7 @@ impl CtrlTask for CtrlRogBios {
if let Ok(lock) = platform2.config.try_lock() {
if platform2.platform.has_panel_od() {
platform2
.set_panel_od(lock.panel_od)
.set_panel_overdrive(lock.panel_od)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
@@ -311,9 +305,8 @@ impl CtrlTask for CtrlRogBios {
)
.await;
self.watch_panel_od(executor, signal_ctxt.clone()).await?;
self.watch_gpu_mux_mode(executor, signal_ctxt.clone())
.await?;
self.watch_panel_od(signal_ctxt.clone()).await?;
self.watch_gpu_mux_mode(signal_ctxt.clone()).await?;
Ok(())
}

View File

@@ -4,7 +4,6 @@ use async_trait::async_trait;
use log::{info, warn};
use rog_platform::power::AsusPower;
use rog_platform::supported::ChargeSupportedFunctions;
use smol::Executor;
use std::sync::Arc;
use std::sync::Mutex;
use zbus::dbus_interface;
@@ -62,12 +61,11 @@ impl CtrlPower {
err
})
.unwrap_or(100);
self.set(limit)
.map_err(|err| {
warn!("CtrlCharge: set_limit {}", err);
err
})
.ok();
if let Ok(mut config) = self.config.try_lock() {
config.read();
config.bat_charge_limit = limit;
config.write();
}
return config.bat_charge_limit;
}
@@ -129,15 +127,10 @@ impl CtrlPower {
#[async_trait]
impl CtrlTask for CtrlPower {
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
signal_ctxt: SignalContext<'a>,
) -> Result<(), RogError> {
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let power1 = self.clone();
let power2 = self.clone();
self.create_sys_event_tasks(
executor,
move || {},
move || {
info!("CtrlCharge reloading charge limit");
@@ -167,8 +160,7 @@ impl CtrlTask for CtrlPower {
)
.await;
self.watch_charge_control_end_threshold(executor, signal_ctxt)
.await?;
self.watch_charge_control_end_threshold(signal_ctxt).await?;
Ok(())
}

View File

@@ -6,7 +6,7 @@ use rog_profiles::fan_curve_set::CurveData;
use rog_profiles::fan_curve_set::FanCurveSet;
use rog_profiles::Profile;
use rog_profiles::PLATFORM_PROFILE;
use smol::Executor;
use zbus::export::futures_util::StreamExt;
use zbus::Connection;
use zbus::SignalContext;
@@ -219,43 +219,39 @@ impl crate::ZbusAdd for ProfileZbus {
#[async_trait]
impl CtrlTask for ProfileZbus {
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
signal_ctxt: SignalContext<'a>,
) -> Result<(), RogError> {
async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
let ctrl = self.inner.clone();
let mut inotify = Inotify::init()?;
inotify.add_watch(PLATFORM_PROFILE, WatchMask::MODIFY)?;
let mut watch = Inotify::init()?;
watch.add_watch(PLATFORM_PROFILE, WatchMask::CLOSE_WRITE)?;
executor
.spawn(async move {
let mut buffer = [0; 1024];
loop {
if let Ok(events) = inotify.read_events_blocking(&mut buffer) {
for _ in events {
let mut active_profile = None;
tokio::spawn(async move {
let mut buffer = [0; 32];
loop {
watch
.event_stream(&mut buffer)
.unwrap()
.for_each(|_| async {
let mut active_profile = None;
if let Ok(ref mut lock) = ctrl.try_lock() {
let new_profile = Profile::get_active_profile().unwrap();
if new_profile != lock.config.active_profile {
lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
active_profile = Some(lock.config.active_profile);
}
}
if let Some(active_profile) = active_profile {
Self::notify_profile(&signal_ctxt, active_profile)
.await
.ok();
if let Ok(ref mut lock) = ctrl.try_lock() {
let new_profile = Profile::get_active_profile().unwrap();
if new_profile != lock.config.active_profile {
lock.config.active_profile = new_profile;
lock.write_profile_curve_to_platform().unwrap();
lock.save_config();
active_profile = Some(lock.config.active_profile);
}
}
}
}
})
.detach();
if let Some(active_profile) = active_profile {
Self::notify_profile(&signal_ctxt.clone(), active_profile)
.await
.ok();
}
})
.await;
}
});
Ok(())
}

View File

@@ -2,11 +2,12 @@ use std::env;
use std::error::Error;
use std::io::Write;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use ::zbus::{Connection, SignalContext};
use log::LevelFilter;
use log::{error, info, warn};
use smol::Executor;
use tokio::time::sleep;
use daemon::ctrl_anime::config::AnimeConfig;
use daemon::ctrl_anime::zbus::CtrlAnimeZbus;
@@ -31,7 +32,8 @@ use rog_profiles::Profile;
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
@@ -60,14 +62,12 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
info!(" rog-profiles v{}", rog_profiles::VERSION);
info!("rog-platform v{}", rog_platform::VERSION);
let mut executor = Executor::new();
smol::block_on(start_daemon(&mut executor))?;
start_daemon().await?;
Ok(())
}
/// The actual main loop for the daemon
async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>> {
async fn start_daemon() -> Result<(), Box<dyn Error>> {
let supported = SupportedFunctions::get_supported();
print_board_info();
println!("{}", serde_json::to_string_pretty(&supported)?);
@@ -90,7 +90,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let task = CtrlRogBios::new(config.clone())?;
let sig = SignalContext::new(&connection, "/org/asuslinux/Platform")?;
task.create_tasks(executor, sig).await.ok();
task.create_tasks(sig).await.ok();
}
Err(err) => {
error!("rog_bios_control: {}", err);
@@ -107,7 +107,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let task = CtrlPower::new(config)?;
let sig = SignalContext::new(&connection, "/org/asuslinux/Charge")?;
task.create_tasks(executor, sig).await.ok();
task.create_tasks(sig).await.ok();
}
Err(err) => {
error!("charge_control: {}", err);
@@ -127,7 +127,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let sig = SignalContext::new(&connection, "/org/asuslinux/Profile")?;
let task = ProfileZbus::new(tmp.clone());
task.create_tasks(executor, sig).await.ok();
task.create_tasks(sig).await.ok();
task.add_to_server(&mut connection).await;
}
Err(err) => {
@@ -152,7 +152,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let task = CtrlAnimeTask::new(inner).await;
let sig = SignalContext::new(&connection, "/org/asuslinux/Anime")?;
task.create_tasks(executor, sig).await.ok();
task.create_tasks(sig).await.ok();
}
Err(err) => {
error!("AniMe control: {}", err);
@@ -176,7 +176,7 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
let task = CtrlKbdLedTask::new(inner);
let sig = SignalContext::new(&connection, "/org/asuslinux/Aura")?;
task.create_tasks(executor, sig).await.ok();
task.create_tasks(sig).await.ok();
}
Err(err) => {
error!("Keyboard control: {}", err);
@@ -185,7 +185,9 @@ async fn start_daemon(executor: &mut Executor<'_>) -> Result<(), Box<dyn Error>>
// Request dbus name after finishing initalizing all functions
connection.request_name(DBUS_NAME).await?;
loop {
smol::block_on(executor.tick());
// This is just a blocker to idle and ensure the reator reacts
sleep(Duration::from_millis(1000)).await;
}
}

View File

@@ -23,12 +23,10 @@ use std::time::Duration;
use crate::error::RogError;
use async_trait::async_trait;
use config::Config;
use inotify::{Inotify, WatchMask};
use log::warn;
use logind_zbus::manager::ManagerProxy;
use smol::{stream::StreamExt, Executor, Timer};
use zbus::{Connection, SignalContext};
use tokio::time;
use zbus::{export::futures_util::StreamExt, Connection, SignalContext};
use zvariant::ObjectPath;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -65,6 +63,8 @@ pub trait ZbusAdd {
/// - `notify_<name>(SignalContext, SomeValue)`
///
/// In most cases if `SomeValue` is stored in a config then `<name>()` getter is expected to update it.
/// The getter should *never* write back to the path or attribute that is being watched or an
/// infinite loop will occur.
///
/// # Example
///
@@ -78,28 +78,24 @@ pub trait ZbusAdd {
macro_rules! task_watch_item {
($name:ident $self_inner:ident) => {
concat_idents::concat_idents!(fn_name = watch_, $name {
async fn fn_name<'a>(
async fn fn_name(
&self,
executor: &mut Executor<'a>,
signal_ctxt: SignalContext<'a>,
signal_ctxt: SignalContext<'static>,
) -> Result<(), RogError> {
use zbus::export::futures_util::StreamExt;
let ctrl = self.clone();
concat_idents::concat_idents!(watch_fn = monitor_, $name {
let mut watch = self.$self_inner.watch_fn()?;
executor
.spawn(async move {
let mut buffer = [0; 1024];
tokio::spawn(async move {
let mut buffer = [0; 32];
watch.event_stream(&mut buffer).unwrap().for_each(|_| async {
let value = ctrl.$name();
dbg!(value);
concat_idents::concat_idents!(notif_fn = notify_, $name {
Self::notif_fn(&signal_ctxt, value).await.unwrap();
});
}).await;
})
.detach();
});
});
Ok(())
}
@@ -112,59 +108,23 @@ macro_rules! task_watch_item {
pub trait CtrlTask {
/// Implement to set up various tasks that may be required, using the `Executor`.
/// No blocking loops are allowed, or they must be run on a separate thread.
async fn create_tasks<'a>(
&self,
executor: &mut Executor<'a>,
signal: SignalContext<'a>,
) -> Result<(), RogError>;
/// Free method to run a task when the path is modified
///
/// Not very useful if you need to also do a zbus notification.
fn create_tasks_inotify(
&self,
executor: &mut Executor,
path: &str,
mut task: impl FnMut() + Send + 'static,
) -> Result<(), RogError> {
let mut inotify = Inotify::init()?;
inotify.add_watch(path, WatchMask::MODIFY)?;
let mut buffer = [0; 1024];
executor
.spawn(async move {
loop {
if let Ok(events) = inotify.read_events_blocking(&mut buffer) {
for _ in events {
task()
}
}
}
})
.detach();
Ok(())
}
async fn create_tasks(&self, signal: SignalContext<'static>) -> Result<(), RogError>;
/// Create a timed repeating task
async fn repeating_task(
&self,
millis: u64,
executor: &mut Executor,
mut task: impl FnMut() + Send + 'static,
) {
let timer = Timer::interval(Duration::from_millis(millis));
executor
.spawn(async move {
timer.for_each(|_| task()).await;
})
.detach();
async fn repeating_task(&self, millis: u64, mut task: impl FnMut() + Send + 'static) {
let mut timer = time::interval(Duration::from_millis(millis));
tokio::spawn(async move {
timer.tick().await;
task();
});
}
/// Free helper method to create tasks to run on: sleep, wake, shutdown, boot
///
/// The closures can potentially block, so execution time should be the minimal possible
/// such as save a variable.
async fn create_sys_event_tasks(
&self,
executor: &mut Executor,
mut on_sleep: impl FnMut() + Send + 'static,
mut on_wake: impl FnMut() + Send + 'static,
mut on_shutdown: impl FnMut() + Send + 'static,
@@ -178,54 +138,40 @@ pub trait CtrlTask {
.await
.expect("Controller could not create ManagerProxy");
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
if args.start {
on_sleep();
} else if !args.start() {
on_wake();
}
}
})
.await;
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_sleep().await {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
on_sleep();
} else if !args.start() {
on_wake();
}
}
}
})
.detach();
}
});
let manager = ManagerProxy::new(&connection)
.await
.expect("Controller could not create ManagerProxy");
executor
.spawn(async move {
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
notif
.for_each(|event| {
if let Ok(args) = event.args() {
if args.start {
on_shutdown();
} else if !args.start() {
on_boot();
}
}
})
.await;
tokio::spawn(async move {
if let Ok(mut notif) = manager.receive_prepare_for_shutdown().await {
while let Some(event) = notif.next().await {
if let Ok(args) = event.args() {
if args.start {
on_shutdown();
} else if !args.start() {
on_boot();
}
}
}
})
.detach();
}
});
}
}
pub trait CtrlTaskComplex {
type A;
fn do_task(&mut self, config: &mut Config, event: Self::A);
}
pub trait GetSupported {
type A;

View File

@@ -156,7 +156,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
start_closed = false;
continue;
}
dbg!("asda");
}
Ok(())
}

View File

@@ -27,7 +27,7 @@ impl eframe::App for AppErrorShow {
.add(Button::new(RichText::new("Okay").size(20.0)))
.clicked()
{
frame.quit();
frame.close();
}
});
});

View File

@@ -39,7 +39,7 @@ impl<'a> RogApp<'a> {
Button::new(RichText::new("").size(height - 4.0)).frame(false),
);
if close_response.clicked() {
frame.quit();
frame.close();
}
});
});

View File

@@ -21,7 +21,6 @@ macro_rules! watch_attr {
pub fn fn_name(&self) -> Result<inotify::Inotify> {
let mut path = self.$item.clone();
path.push($attr_name);
dbg!(&path);
let mut inotify = inotify::Inotify::init().unwrap();
inotify.add_watch(path.to_str().unwrap(), inotify::WatchMask::MODIFY).unwrap();
Ok(inotify)