mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
daemon: Vastly improved task creation
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod zbus;
|
pub mod zbus;
|
||||||
|
|
||||||
use ::zbus::Connection;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logind_zbus::manager::ManagerProxy;
|
|
||||||
use rog_anime::{
|
use rog_anime::{
|
||||||
error::AnimeError,
|
error::AnimeError,
|
||||||
usb::{
|
usb::{
|
||||||
@@ -14,7 +12,7 @@ use rog_anime::{
|
|||||||
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
|
ActionData, AnimeDataBuffer, AnimePacketType, AnimeType,
|
||||||
};
|
};
|
||||||
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw};
|
use rog_platform::{hid_raw::HidRaw, supported::AnimeSupportedFunctions, usb_raw::USBRaw};
|
||||||
use smol::{stream::StreamExt, Executor};
|
use smol::Executor;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
@@ -232,14 +230,6 @@ impl CtrlAnimeTask {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl crate::CtrlTask for CtrlAnimeTask {
|
impl crate::CtrlTask for CtrlAnimeTask {
|
||||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
let connection = Connection::system()
|
|
||||||
.await
|
|
||||||
.expect("CtrlAnimeTask could not create dbus connection");
|
|
||||||
|
|
||||||
let manager = ManagerProxy::new(&connection)
|
|
||||||
.await
|
|
||||||
.expect("CtrlAnimeTask could not create ManagerProxy");
|
|
||||||
|
|
||||||
let run_action =
|
let run_action =
|
||||||
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
|
|start: bool, lock: MutexGuard<CtrlAnime>, inner: Arc<Mutex<CtrlAnime>>| {
|
||||||
if start {
|
if start {
|
||||||
@@ -251,50 +241,40 @@ impl crate::CtrlTask for CtrlAnimeTask {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = self.inner.clone();
|
let inner1 = self.inner.clone();
|
||||||
executor
|
let inner2 = self.inner.clone();
|
||||||
.spawn(async move {
|
let inner3 = self.inner.clone();
|
||||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
let inner4 = self.inner.clone();
|
||||||
notif
|
self.create_sys_event_tasks(
|
||||||
.for_each(|event| {
|
executor,
|
||||||
if let Ok(args) = event.args() {
|
// Loop is required to try an attempt to get the mutex *without* blocking
|
||||||
// Loop is required to try an attempt to get the mutex *without* blocking
|
// other threads - it is possible to end up with deadlocks otherwise.
|
||||||
// other threads - it is possible to end up with deadlocks otherwise.
|
move || loop {
|
||||||
loop {
|
if let Ok(lock) = inner1.clone().try_lock() {
|
||||||
if let Ok(lock) = inner.clone().try_lock() {
|
run_action(true, lock, inner1.clone());
|
||||||
run_action(args.start, lock, inner.clone());
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
move || loop {
|
||||||
|
if let Ok(lock) = inner2.clone().try_lock() {
|
||||||
let manager = ManagerProxy::new(&connection)
|
run_action(false, lock, inner2.clone());
|
||||||
.await
|
break;
|
||||||
.expect("CtrlAnimeTask could not create ManagerProxy");
|
|
||||||
|
|
||||||
let inner = self.inner.clone();
|
|
||||||
executor
|
|
||||||
.spawn(async move {
|
|
||||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
|
||||||
notif
|
|
||||||
.for_each(|event| {
|
|
||||||
if let Ok(args) = event.args() {
|
|
||||||
loop {
|
|
||||||
if let Ok(lock) = inner.clone().try_lock() {
|
|
||||||
run_action(args.start, lock, inner.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
move || loop {
|
||||||
|
if let Ok(lock) = inner3.clone().try_lock() {
|
||||||
|
run_action(true, lock, inner3.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move || loop {
|
||||||
|
if let Ok(lock) = inner4.clone().try_lock() {
|
||||||
|
run_action(false, lock, inner4.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,17 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logind_zbus::manager::ManagerProxy;
|
|
||||||
use rog_aura::{
|
use rog_aura::{
|
||||||
usb::{AuraDevice, LED_APPLY, LED_SET},
|
usb::{AuraDevice, LED_APPLY, LED_SET},
|
||||||
AuraEffect, LedBrightness, LED_MSG_LEN,
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
};
|
};
|
||||||
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
|
use rog_aura::{AuraZone, Direction, Speed, GRADIENT};
|
||||||
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
|
use rog_platform::{hid_raw::HidRaw, keyboard_led::KeyboardLed, supported::LedSupportedFunctions};
|
||||||
use smol::{stream::StreamExt, Executor};
|
use smol::Executor;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
use zbus::Connection;
|
|
||||||
|
|
||||||
use crate::GetSupported;
|
use crate::GetSupported;
|
||||||
|
|
||||||
@@ -95,14 +93,6 @@ impl CtrlKbdLedTask {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CtrlTask for CtrlKbdLedTask {
|
impl CtrlTask for CtrlKbdLedTask {
|
||||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
let connection = Connection::system()
|
|
||||||
.await
|
|
||||||
.expect("CtrlKbdLedTask could not create dbus connection");
|
|
||||||
|
|
||||||
let manager = ManagerProxy::new(&connection)
|
|
||||||
.await
|
|
||||||
.expect("CtrlKbdLedTask could not create ManagerProxy");
|
|
||||||
|
|
||||||
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
|
let load_save = |start: bool, mut lock: MutexGuard<CtrlKbdLed>| {
|
||||||
// If waking up
|
// If waking up
|
||||||
if !start {
|
if !start {
|
||||||
@@ -121,41 +111,41 @@ impl CtrlTask for CtrlKbdLedTask {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = self.inner.clone();
|
let inner1 = self.inner.clone();
|
||||||
executor
|
let inner2 = self.inner.clone();
|
||||||
.spawn(async move {
|
let inner3 = self.inner.clone();
|
||||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
let inner4 = self.inner.clone();
|
||||||
notif
|
self.create_sys_event_tasks(
|
||||||
.for_each(|event| {
|
executor,
|
||||||
if let Ok(args) = event.args() {
|
// Loop so that we do aquire the lock but also don't block other
|
||||||
loop {
|
// threads (prevents potential deadlocks)
|
||||||
// Loop so that we do aquire the lock but also don't block other
|
move || loop {
|
||||||
// threads (prevents potential deadlocks)
|
if let Ok(lock) = inner1.clone().try_lock() {
|
||||||
if let Ok(lock) = inner.clone().try_lock() {
|
load_save(true, lock);
|
||||||
load_save(args.start, lock);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
},
|
||||||
notif
|
move || loop {
|
||||||
.for_each(|event| {
|
if let Ok(lock) = inner2.clone().try_lock() {
|
||||||
if let Ok(args) = event.args() {
|
load_save(false, lock);
|
||||||
loop {
|
break;
|
||||||
if let Ok(lock) = inner.clone().try_lock() {
|
|
||||||
load_save(args.start, lock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
move || loop {
|
||||||
|
if let Ok(lock) = inner3.clone().try_lock() {
|
||||||
|
load_save(false, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move || loop {
|
||||||
|
if let Ok(lock) = inner4.clone().try_lock() {
|
||||||
|
load_save(false, lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ use crate::CtrlTask;
|
|||||||
use crate::{config::Config, error::RogError, GetSupported};
|
use crate::{config::Config, error::RogError, GetSupported};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use logind_zbus::manager::ManagerProxy;
|
|
||||||
use rog_platform::platform::{AsusPlatform, GpuMode};
|
use rog_platform::platform::{AsusPlatform, GpuMode};
|
||||||
use rog_platform::supported::RogBiosSupportedFunctions;
|
use rog_platform::supported::RogBiosSupportedFunctions;
|
||||||
use smol::stream::StreamExt;
|
|
||||||
use smol::Executor;
|
use smol::Executor;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@@ -260,71 +258,39 @@ impl crate::Reloadable for CtrlRogBios {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CtrlTask for CtrlRogBios {
|
impl CtrlTask for CtrlRogBios {
|
||||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
let connection = Connection::system()
|
let platform1 = self.clone();
|
||||||
.await
|
let platform2 = self.clone();
|
||||||
.expect("CtrlRogBios could not create dbus connection");
|
self.create_sys_event_tasks(
|
||||||
|
executor,
|
||||||
let manager = ManagerProxy::new(&connection)
|
move || {},
|
||||||
.await
|
move || {
|
||||||
.expect("CtrlRogBios could not create ManagerProxy");
|
info!("CtrlRogBios reloading panel_od");
|
||||||
|
if let Ok(lock) = platform1.config.try_lock() {
|
||||||
let platform = self.clone();
|
platform1
|
||||||
executor
|
.set_panel_od(lock.panel_od)
|
||||||
.spawn(async move {
|
.map_err(|err| {
|
||||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
warn!("CtrlCharge: set_limit {}", err);
|
||||||
notif
|
err
|
||||||
.for_each(|event| {
|
|
||||||
if let Ok(args) = event.args() {
|
|
||||||
// If waking up
|
|
||||||
if !args.start {
|
|
||||||
info!("CtrlRogBios reloading panel_od");
|
|
||||||
if let Ok(lock) = platform.config.try_lock() {
|
|
||||||
platform
|
|
||||||
.set_panel_od(lock.panel_od)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlCharge: set_limit {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await;
|
.ok();
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
move || {},
|
||||||
|
move || {
|
||||||
let manager = ManagerProxy::new(&connection)
|
info!("CtrlRogBios reloading panel_od");
|
||||||
.await
|
if let Ok(lock) = platform2.config.try_lock() {
|
||||||
.expect("CtrlCharge could not create ManagerProxy");
|
platform2
|
||||||
|
.set_panel_od(lock.panel_od)
|
||||||
let platform = self.clone();
|
.map_err(|err| {
|
||||||
executor
|
warn!("CtrlCharge: set_limit {}", err);
|
||||||
.spawn(async move {
|
err
|
||||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
|
||||||
notif
|
|
||||||
.for_each(|event| {
|
|
||||||
if let Ok(args) = event.args() {
|
|
||||||
// If waking up - intention is to catch hibernation event
|
|
||||||
if !args.start {
|
|
||||||
info!("CtrlRogBios reloading panel_od");
|
|
||||||
if let Ok(lock) = platform.config.try_lock() {
|
|
||||||
platform
|
|
||||||
.set_panel_od(lock.panel_od)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlCharge: set_limit {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await;
|
.ok();
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ use crate::CtrlTask;
|
|||||||
use crate::{config::Config, error::RogError, GetSupported};
|
use crate::{config::Config, error::RogError, GetSupported};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use logind_zbus::manager::ManagerProxy;
|
|
||||||
use rog_platform::power::AsusPower;
|
use rog_platform::power::AsusPower;
|
||||||
use rog_platform::supported::ChargeSupportedFunctions;
|
use rog_platform::supported::ChargeSupportedFunctions;
|
||||||
use smol::stream::StreamExt;
|
|
||||||
use smol::Executor;
|
use smol::Executor;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@@ -111,71 +109,39 @@ impl CtrlPower {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CtrlTask for CtrlPower {
|
impl CtrlTask for CtrlPower {
|
||||||
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
async fn create_tasks(&self, executor: &mut Executor) -> Result<(), RogError> {
|
||||||
let connection = Connection::system()
|
let power1 = self.clone();
|
||||||
.await
|
let power2 = self.clone();
|
||||||
.expect("CtrlCharge could not create dbus connection");
|
self.create_sys_event_tasks(
|
||||||
|
executor,
|
||||||
let manager = ManagerProxy::new(&connection)
|
move || {},
|
||||||
.await
|
move || {
|
||||||
.expect("CtrlCharge could not create ManagerProxy");
|
info!("CtrlCharge reloading charge limit");
|
||||||
|
if let Ok(lock) = power1.config.try_lock() {
|
||||||
let power = self.clone();
|
power1
|
||||||
executor
|
.set(lock.bat_charge_limit)
|
||||||
.spawn(async move {
|
.map_err(|err| {
|
||||||
if let Ok(notif) = manager.receive_prepare_for_sleep().await {
|
warn!("CtrlCharge: set_limit {}", err);
|
||||||
notif
|
err
|
||||||
.for_each(|event| {
|
|
||||||
if let Ok(args) = event.args() {
|
|
||||||
// If waking up
|
|
||||||
if !args.start {
|
|
||||||
info!("CtrlCharge reloading charge limit");
|
|
||||||
if let Ok(lock) = power.config.try_lock() {
|
|
||||||
power
|
|
||||||
.set(lock.bat_charge_limit)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlCharge: set_limit {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await;
|
.ok();
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
move || {},
|
||||||
|
move || {
|
||||||
let manager = ManagerProxy::new(&connection)
|
info!("CtrlCharge reloading charge limit");
|
||||||
.await
|
if let Ok(lock) = power2.config.try_lock() {
|
||||||
.expect("CtrlCharge could not create ManagerProxy");
|
power2
|
||||||
|
.set(lock.bat_charge_limit)
|
||||||
let power = self.clone();
|
.map_err(|err| {
|
||||||
executor
|
warn!("CtrlCharge: set_limit {}", err);
|
||||||
.spawn(async move {
|
err
|
||||||
if let Ok(notif) = manager.receive_prepare_for_shutdown().await {
|
|
||||||
notif
|
|
||||||
.for_each(|event| {
|
|
||||||
if let Ok(args) = event.args() {
|
|
||||||
// If waking up - intention is to catch hibernation event
|
|
||||||
if !args.start {
|
|
||||||
info!("CtrlCharge reloading charge limit");
|
|
||||||
if let Ok(lock) = power.config.try_lock() {
|
|
||||||
power
|
|
||||||
.set(lock.bat_charge_limit)
|
|
||||||
.map_err(|err| {
|
|
||||||
warn!("CtrlCharge: set_limit {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await;
|
.ok();
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.detach();
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use crate::error::RogError;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use logind_zbus::manager::ManagerProxy;
|
||||||
use smol::{stream::StreamExt, Executor, Timer};
|
use smol::{stream::StreamExt, Executor, Timer};
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
use zvariant::ObjectPath;
|
use zvariant::ObjectPath;
|
||||||
@@ -87,6 +88,64 @@ pub trait CtrlTask {
|
|||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Free helper method to create tasks to run on: sleep, wake, shutdown, boot
|
||||||
|
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,
|
||||||
|
mut on_boot: impl FnMut() + Send + 'static,
|
||||||
|
) {
|
||||||
|
let connection = Connection::system()
|
||||||
|
.await
|
||||||
|
.expect("Controller could not create dbus connection");
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection)
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CtrlTaskComplex {
|
pub trait CtrlTaskComplex {
|
||||||
|
|||||||
@@ -271,8 +271,7 @@ mod tests {
|
|||||||
"CPU: 30c:1%,49c:1%,59c:3%,69c:3%,79c:30%,89c:49%,99c:56%,109c:58%"
|
"CPU: 30c:1%,49c:1%,59c:3%,69c:3%,79c:30%,89c:49%,99c:56%,109c:58%"
|
||||||
);
|
);
|
||||||
|
|
||||||
let curve =
|
let curve = CurveData::from_str("30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%");
|
||||||
CurveData::from_str("30c:1%,49c:2%,59c:3%,69c:4%,79c:31%,89c:49%,99c:56%");
|
|
||||||
|
|
||||||
assert!(curve.is_err());
|
assert!(curve.is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user