Add basic async support over threads

This commit is contained in:
Demmie 2023-06-17 02:14:09 -04:00
parent 5685631868
commit 790277071b
No known key found for this signature in database
GPG Key ID: B06DAA3D432C6E9A
6 changed files with 109 additions and 133 deletions

View File

@ -33,19 +33,19 @@ fern = { version = "0.6.2", features = ["colored"] }
log = { workspace = true } log = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
signal-hook = "0.3.15" signal-hook = "0.3.15"
tokio = { version = "1.28.2", features = ["rt", "macros"] }
ksni = {version = "0.2.0", optional = true} ksni = {version = "0.2.0", optional = true}
aw-server = { git = "https://github.com/2e3s/aw-server-rust", optional = true, rev = "1c23a55" } aw-server = { git = "https://github.com/2e3s/aw-server-rust", optional = true, rev = "1c23a55" }
aw-datastore = { git = "https://github.com/2e3s/aw-server-rust", optional = true, rev = "1c23a55" } aw-datastore = { git = "https://github.com/2e3s/aw-server-rust", optional = true, rev = "1c23a55" }
open = { version = "4.1.0", optional = true } open = { version = "4.1.0", optional = true }
rust-embed = { version = "6.6.1", features = ["interpolate-folder-path"] } rust-embed = { version = "6.6.1", features = ["interpolate-folder-path"], optional = true }
tokio = { version = "1.28.2", optional = true }
[features] [features]
default = ["gnome", "kwin_window"] default = ["gnome", "kwin_window"]
gnome = ["watchers/gnome"] gnome = ["watchers/gnome"]
kwin_window = ["watchers/kwin_window"] kwin_window = ["watchers/kwin_window"]
bundle = ["ksni", "tokio", "aw-server", "aw-datastore", "open"] bundle = ["ksni", "aw-server", "aw-datastore", "open", "rust-embed"]
[package.metadata.deb] [package.metadata.deb]
features = ["bundle"] features = ["bundle"]

View File

@ -6,19 +6,23 @@ use std::{
path::PathBuf, path::PathBuf,
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
}; };
use watchers::config::Config;
pub fn run(config: &Config, config_file: PathBuf, no_tray: bool, is_stopped: Arc<AtomicBool>) { pub async fn run(
host: String,
port: u32,
config_file: PathBuf,
no_tray: bool,
is_stopped: Arc<AtomicBool>,
) {
if !no_tray { if !no_tray {
let service = ksni::TrayService::new(Tray { let service = ksni::TrayService::new(Tray {
server_host: config.host.clone(), server_host: host,
server_port: config.port, server_port: port,
config_file, config_file,
is_stopped: Arc::clone(&is_stopped), is_stopped: Arc::clone(&is_stopped),
}); });
service.spawn(); service.spawn();
} }
let port = config.port; server::run(port, is_stopped).await;
server::run(port, is_stopped);
} }

View File

@ -6,8 +6,7 @@ use std::sync::{
}; };
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
pub fn run(port: u32, is_stopped: Arc<AtomicBool>) { pub async fn run(port: u32, is_stopped: Arc<AtomicBool>) {
std::thread::spawn(move || {
let db_path = aw_server::dirs::db_path(false) let db_path = aw_server::dirs::db_path(false)
.map_err(|_| anyhow!("DB path is not found")) .map_err(|_| anyhow!("DB path is not found"))
.unwrap() .unwrap()
@ -38,15 +37,8 @@ pub fn run(port: u32, is_stopped: Arc<AtomicBool>) {
} }
}; };
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
tokio::select! ( tokio::select! (
r = server => {r.unwrap();}, r = server => {r.unwrap();},
_ = check => {}, _ = check => {},
); );
});
});
} }

View File

@ -10,10 +10,11 @@ mod config;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
use watchers::ConstructorFilter; use watchers::run_first_supported;
use watchers::ReportClient; use watchers::ReportClient;
fn main() -> anyhow::Result<()> { #[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let is_stopped = Arc::new(AtomicBool::new(false)); let is_stopped = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&is_stopped))?; signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&is_stopped))?;
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&is_stopped))?; signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&is_stopped))?;
@ -40,33 +41,27 @@ fn main() -> anyhow::Result<()> {
config.poll_time_window.as_secs() config.poll_time_window.as_secs()
); );
#[cfg(feature = "bundle")]
bundle::run(&config, config_file, no_tray, Arc::clone(&is_stopped));
let client = ReportClient::new(config)?; let client = ReportClient::new(config)?;
let client = Arc::new(client); let client = Arc::new(client);
let mut thread_handlers = Vec::new(); let idle_handler = run_first_supported(watchers::IDLE, &client, Arc::clone(&is_stopped));
let active_window_handler =
run_first_supported(watchers::ACTIVE_WINDOW, &client, Arc::clone(&is_stopped));
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client, Arc::clone(&is_stopped)) #[cfg(not(feature = "bundle"))]
{ {
thread_handlers.push(idle_handler); tokio::select!(
} else { _ = idle_handler => Ok(()),
warn!("No supported idle handler is found"); _ = active_window_handler => Ok(()),
)
} }
if let Some(active_window_handler) = #[cfg(feature = "bundle")]
watchers::ACTIVE_WINDOW.run_first_supported(&client, is_stopped)
{ {
thread_handlers.push(active_window_handler); tokio::select!(
} else { _ = idle_handler => Ok(()),
warn!("No supported active window handler is found"); _ = active_window_handler => Ok(()),
_ = bundle::run(client.config.host.clone(), client.config.port, config_file, no_tray, Arc::clone(&is_stopped)) => Ok(()),
)
} }
for handler in thread_handlers {
if handler.join().is_err() {
error!("Thread failed with error");
}
}
Ok(())
} }

View File

@ -6,7 +6,7 @@ mod report_client;
mod watchers; mod watchers;
pub use crate::report_client::ReportClient; pub use crate::report_client::ReportClient;
pub use crate::watchers::ConstructorFilter; pub use crate::watchers::run_first_supported;
pub use crate::watchers::Watcher; pub use crate::watchers::Watcher;
pub use crate::watchers::ACTIVE_WINDOW; pub use crate::watchers::ACTIVE_WINDOW;
pub use crate::watchers::IDLE; pub use crate::watchers::IDLE;

View File

@ -20,7 +20,7 @@ use std::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc,
}, },
thread::{self, JoinHandle}, thread,
time::Duration, time::Duration,
}; };
@ -52,25 +52,6 @@ pub trait Watcher: Send {
where where
Self: Sized; Self: Sized;
fn run(
&mut self,
watcher_type: &WatcherType,
client: &Arc<ReportClient>,
is_stopped: Arc<AtomicBool>,
) {
info!("Starting {watcher_type} watcher");
loop {
if is_stopped.load(Ordering::Relaxed) {
warn!("Received an exit signal, shutting down {watcher_type}");
break;
}
if let Err(e) = self.run_iteration(client) {
error!("Error on {watcher_type} iteration: {e}");
}
thread::sleep(watcher_type.sleep_time(&client.config));
}
}
fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()>; fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()>;
} }
@ -81,25 +62,12 @@ type WatcherConstructors = [(
fn(&Arc<ReportClient>) -> anyhow::Result<BoxedWatcher>, fn(&Arc<ReportClient>) -> anyhow::Result<BoxedWatcher>,
)]; )];
pub trait ConstructorFilter { pub fn filter_first_supported(
fn filter_first_supported( watcher_constructors: &'static WatcherConstructors,
&self,
client: &Arc<ReportClient>, client: &Arc<ReportClient>,
) -> Option<(&WatcherType, BoxedWatcher)>; ) -> Option<(&'static WatcherType, BoxedWatcher)> {
watcher_constructors
fn run_first_supported( .iter()
&'static self,
client: &Arc<ReportClient>,
is_stopped: Arc<AtomicBool>,
) -> Option<JoinHandle<()>>;
}
impl ConstructorFilter for WatcherConstructors {
fn filter_first_supported(
&self,
client: &Arc<ReportClient>,
) -> Option<(&WatcherType, BoxedWatcher)> {
self.iter()
.find_map(|(name, watcher_type, watcher)| match watcher(client) { .find_map(|(name, watcher_type, watcher)| match watcher(client) {
Ok(watcher) => { Ok(watcher) => {
info!("Selected {name} as {watcher_type} watcher"); info!("Selected {name} as {watcher_type} watcher");
@ -110,22 +78,39 @@ impl ConstructorFilter for WatcherConstructors {
None None
} }
}) })
} }
fn run_first_supported( async fn run_watcher(
&'static self, watcher: &mut Box<dyn Watcher>,
watcher_type: &WatcherType,
client: &Arc<ReportClient>, client: &Arc<ReportClient>,
is_stopped: Arc<AtomicBool>, is_stopped: Arc<AtomicBool>,
) -> Option<JoinHandle<()>> { ) {
let idle_watcher = self.filter_first_supported(client); info!("Starting {watcher_type} watcher");
if let Some((watcher_type, mut watcher)) = idle_watcher { loop {
let thread_client = Arc::clone(client); if is_stopped.load(Ordering::Relaxed) {
let idle_handler = warn!("Received an exit signal, shutting down {watcher_type}");
thread::spawn(move || watcher.run(watcher_type, &thread_client, is_stopped)); break;
Some(idle_handler)
} else {
None
} }
if let Err(e) = watcher.run_iteration(client) {
error!("Error on {watcher_type} iteration: {e}");
}
thread::sleep(watcher_type.sleep_time(&client.config));
}
}
pub async fn run_first_supported(
watcher_constructors: &'static WatcherConstructors,
client: &Arc<ReportClient>,
is_stopped: Arc<AtomicBool>,
) -> bool {
let supported_watcher = filter_first_supported(watcher_constructors, client);
if let Some((watcher_type, mut watcher)) = supported_watcher {
let thread_client = Arc::clone(client);
run_watcher(&mut watcher, watcher_type, &thread_client, is_stopped).await;
true
} else {
false
} }
} }