From 1487ad5bd3aecbb4cc452525417b05723595bd30 Mon Sep 17 00:00:00 2001 From: Hannes Kuchelmeister Date: Sun, 22 May 2022 13:27:53 +0200 Subject: [PATCH] implement background message handler (worker) --- Cargo.lock | 185 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 7 +- src/ui/app/messages.rs | 3 + src/ui/app/mod.rs | 1 + src/ui/app/model.rs | 31 +++++-- src/ui/app/toolbox_list.rs | 28 +++--- src/ui/app/update.rs | 29 ++++-- src/ui/app/widgets.rs | 2 +- src/ui/app/workers.rs | 67 ++++++++++++++ src/ui/components/mod.rs | 3 + 11 files changed, 323 insertions(+), 34 deletions(-) create mode 100644 src/ui/app/workers.rs diff --git a/Cargo.lock b/Cargo.lock index bf0c275..ec75a85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cairo-rs" version = "0.15.10" @@ -389,6 +395,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "libadwaita" version = "0.1.1" @@ -427,6 +442,16 @@ version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -451,6 +476,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[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 = "once_cell" version = "1.10.0" @@ -482,6 +529,29 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "pest" version = "2.1.3" @@ -570,6 +640,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + [[package]] name = "relm4" version = "0.4.3" @@ -605,6 +684,12 @@ dependencies = [ "semver", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "0.11.0" @@ -629,6 +714,15 @@ version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.5" @@ -641,6 +735,16 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "syn" version = "1.0.89" @@ -685,6 +789,37 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio" +version = "1.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.5.8" @@ -699,6 +834,7 @@ name = "toolbox-tuner" version = "0.0.0" dependencies = [ "relm4", + "tokio", ] [[package]] @@ -725,6 +861,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -746,3 +888,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index a46690f..cda2fb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] relm4 = {version="0.4", features = ["libadwaita", "macros"]} +tokio = { version = "1", features = ["full"] } [package.metadata.appimage] auto_link = true diff --git a/src/main.rs b/src/main.rs index 9e218ac..4e0cb79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,6 @@ use std::collections::VecDeque; -use relm4::{ - factory::{FactoryVecDeque}, - RelmApp, -}; +use relm4::{factory::FactoryVecDeque, RelmApp}; use toolbx::ToolbxContainer; use ui::app::model::AppModel; @@ -13,7 +10,7 @@ mod ui; fn main() { let toolbx_list = VecDeque::from(ToolbxContainer::get_toolboxes()); let factory_vec = FactoryVecDeque::new(); - + let mut model = AppModel { toolboxes: factory_vec, }; diff --git a/src/ui/app/messages.rs b/src/ui/app/messages.rs index 855a01b..2a0ca4d 100644 --- a/src/ui/app/messages.rs +++ b/src/ui/app/messages.rs @@ -1,7 +1,10 @@ use relm4::factory::DynamicIndex; +use super::model::ToolbxEntry; + pub enum AppMsg { ShowToolboxSettingsRequest, ShowToolboxAppsRequest, ToolbxContainerToggleStartStop(DynamicIndex), + ToolbxContainerChanged(DynamicIndex, ToolbxEntry), } diff --git a/src/ui/app/mod.rs b/src/ui/app/mod.rs index 07a1d51..caa508a 100644 --- a/src/ui/app/mod.rs +++ b/src/ui/app/mod.rs @@ -3,3 +3,4 @@ pub mod model; pub mod toolbox_list; pub mod update; pub mod widgets; +pub mod workers; diff --git a/src/ui/app/model.rs b/src/ui/app/model.rs index ccade82..dac6a01 100644 --- a/src/ui/app/model.rs +++ b/src/ui/app/model.rs @@ -1,12 +1,10 @@ -use relm4::{ - factory::{FactoryVecDeque}, - Model, -}; +use relm4::{factory::FactoryVecDeque, Model}; use crate::{toolbx::ToolbxContainer, ui::components::AppComponents}; use super::{messages::AppMsg, widgets::AppWidgets}; +#[derive(Debug, Clone)] pub struct ToolbxEntry { pub toolbx_container: ToolbxContainer, pub changing_status: bool, @@ -14,16 +12,23 @@ pub struct ToolbxEntry { } impl ToolbxEntry { - pub fn update_container(&mut self, container : ToolbxContainer) { + pub fn update_container(&mut self, container: ToolbxContainer) { std::mem::replace::(&mut self.toolbx_container, container); } + pub fn update_entry(&mut self, container: ToolbxEntry) { + std::mem::replace::( + &mut self.toolbx_container, + container.toolbx_container, + ); + self.changing_status = container.changing_status; + } } pub struct AppModel { pub toolboxes: FactoryVecDeque, } -impl Model for AppModel { +impl Model for AppModel { type Msg = AppMsg; type Widgets = AppWidgets; type Components = AppComponents; @@ -32,7 +37,8 @@ impl Model for AppModel { impl AppModel { pub fn update_toolbxes(&mut self, toolbox_iter: I) where - I: Iterator, { + I: Iterator, + { // Update each toolbx entry if there were changes to it // TODO: deal with the removal of toolboxes for tbx_update in toolbox_iter { @@ -40,15 +46,20 @@ impl AppModel { let mut exists = false; for (index, tbx_entry) in self.toolboxes.iter().enumerate() { if tbx_update.name == tbx_entry.toolbx_container.name { - self.toolboxes.get_mut(index).map(|x| x.update_container(tbx_update.clone())); + self.toolboxes + .get_mut(index) + .map(|x| x.update_container(tbx_update.clone())); exists = true; break; } } if !exists { println!("{}", tbx_update.name); - self.toolboxes.push_back(ToolbxEntry { toolbx_container: tbx_update, changing_status: false}) + self.toolboxes.push_back(ToolbxEntry { + toolbx_container: tbx_update, + changing_status: false, + }) } } } -} \ No newline at end of file +} diff --git a/src/ui/app/toolbox_list.rs b/src/ui/app/toolbox_list.rs index ccf4698..0b192b5 100644 --- a/src/ui/app/toolbox_list.rs +++ b/src/ui/app/toolbox_list.rs @@ -9,7 +9,7 @@ use relm4::{ }; use crate::{ - toolbx::{ToolbxContainer, ToolbxStatus}, + toolbx::ToolbxStatus, ui::ui_strings::{ APP_ICON, APP_TOOLTIP, SETTINGS_ICON, SETTINGS_TOOLTIP, SHUTDOWN_ICON, SHUTDOWN_TOOLTIP, START_ICON, START_TOOLTIP, TERMINAL_ICON, TERMINAL_TOOLTIP, UPDATE_ICON, UPDATE_TOOLTIP, @@ -33,7 +33,6 @@ impl FactoryPrototype for ToolbxEntry { type Msg = AppMsg; fn init_view(&self, key: &DynamicIndex, sender: Sender) -> Self::Widgets { - view! { suffix_box = >k::Box{ append = >k::AspectFrame{ @@ -107,7 +106,10 @@ impl FactoryPrototype for ToolbxEntry { } } - let subtitle = format!("created {}\n{}", self.toolbx_container.created, self.toolbx_container.image); + let subtitle = format!( + "created {}\n{}", + self.toolbx_container.created, self.toolbx_container.image + ); let index = key.clone(); @@ -117,7 +119,7 @@ impl FactoryPrototype for ToolbxEntry { set_margin_bottom: 10, set_tooltip_text: Some(status_button_tooltip), set_css_classes: &["circular"], - + } }; //status_spinner.start(); @@ -150,7 +152,11 @@ impl FactoryPrototype for ToolbxEntry { } }; - FactoryWidgets { action_row, status_button, status_spinner} + FactoryWidgets { + action_row, + status_button, + status_spinner, + } } fn view( @@ -160,19 +166,21 @@ impl FactoryPrototype for ToolbxEntry { ) { println!("updated {}", key.current_index()); - // fixme: IDEALY this is would be done with message handling and only if the request actually is done if self.changing_status { widgets.status_button.set_sensitive(false); - widgets.status_button.set_child(Some(&widgets.status_spinner)); + widgets + .status_button + .set_child(Some(&widgets.status_spinner)); widgets.status_spinner.start(); - } else { match self.toolbx_container.status { ToolbxStatus::Running => { widgets.status_button.set_icon_name(SHUTDOWN_ICON); - widgets.status_button.set_tooltip_text(Some(SHUTDOWN_TOOLTIP)); + widgets + .status_button + .set_tooltip_text(Some(SHUTDOWN_TOOLTIP)); } _ => { widgets.status_button.set_icon_name(START_ICON); @@ -182,8 +190,6 @@ impl FactoryPrototype for ToolbxEntry { widgets.status_button.set_sensitive(true); widgets.status_spinner.stop(); } - - } fn root_widget(widgets: &Self::Widgets) -> &Self::Root { diff --git a/src/ui/app/update.rs b/src/ui/app/update.rs index dd423f5..5dc2da1 100644 --- a/src/ui/app/update.rs +++ b/src/ui/app/update.rs @@ -8,7 +8,7 @@ use crate::{ }, }; -use super::{messages::AppMsg, model::AppModel}; +use super::{messages::AppMsg, model::AppModel, workers::AsyncHandlerMsg}; impl AppUpdate for AppModel { fn update(&mut self, msg: AppMsg, components: &AppComponents, _sender: Sender) -> bool { @@ -27,23 +27,38 @@ impl AppUpdate for AppModel { } AppMsg::ToolbxContainerToggleStartStop(index) => { if let Some(toolbx_container) = self.toolboxes.get_mut(index.current_index()) { - match toolbx_container.toolbx_container.status { ToolbxStatus::Exited | ToolbxStatus::Configured => { toolbx_container.changing_status = true; - // Send message to background worker to start - //toolbx_container.toolbx_container.start(); - + components + .async_handler + .sender() + .blocking_send(AsyncHandlerMsg::StartToolbx( + index, + toolbx_container.clone(), + )) + .expect("Receiver dropped"); } ToolbxStatus::Running => { toolbx_container.changing_status = true; - // send message to beackground worker to stop - //toolbx_container.toolbx_container.stop(); + components + .async_handler + .sender() + .blocking_send(AsyncHandlerMsg::StopToolbx( + index, + toolbx_container.clone(), + )) + .expect("Receiver dropped"); } } // TODO: tell button to reactivate somehow } } + AppMsg::ToolbxContainerChanged(index, container) => { + if let Some(toolbx_container) = self.toolboxes.get_mut(index.current_index()) { + toolbx_container.update_entry(container); + } + } } true } diff --git a/src/ui/app/widgets.rs b/src/ui/app/widgets.rs index 85159e4..6cc42fb 100644 --- a/src/ui/app/widgets.rs +++ b/src/ui/app/widgets.rs @@ -4,7 +4,7 @@ use relm4::{ prelude::{BoxExt, GtkWindowExt, OrientableExt, WidgetExt}, traits::AdwApplicationWindowExt, }, - gtk::{self, SelectionMode, Align, PolicyType}, + gtk::{self, Align, PolicyType, SelectionMode}, WidgetPlus, Widgets, }; diff --git a/src/ui/app/workers.rs b/src/ui/app/workers.rs new file mode 100644 index 0000000..2722cb3 --- /dev/null +++ b/src/ui/app/workers.rs @@ -0,0 +1,67 @@ +use relm4::factory::DynamicIndex; +use relm4::{send, MessageHandler, Sender}; +use tokio::runtime::{Builder, Runtime}; +use tokio::sync::mpsc::{channel, Sender as TokioSender}; + +use super::{ + messages::AppMsg, + model::{AppModel, ToolbxEntry}, +}; + +// Code adapted from https://relm4.org/book/stable/message_handler.html +pub struct AsyncHandler { + _rt: Runtime, + sender: TokioSender, +} + +#[derive(Debug)] +pub enum AsyncHandlerMsg { + StopToolbx(DynamicIndex, ToolbxEntry), + StartToolbx(DynamicIndex, ToolbxEntry), +} + +impl MessageHandler for AsyncHandler { + type Msg = AsyncHandlerMsg; + type Sender = TokioSender; + + fn init(_parent_model: &AppModel, parent_sender: Sender) -> Self { + let (sender, mut rx) = channel::(10); + + let rt = Builder::new_multi_thread() + .worker_threads(8) + .enable_time() + .build() + .unwrap(); + + rt.spawn(async move { + while let Some(msg) = rx.recv().await { + let parent_sender = parent_sender.clone(); + tokio::spawn(async move { + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + match msg { + AsyncHandlerMsg::StopToolbx(index, mut tbx) => { + tbx.toolbx_container.stop(); + tbx.changing_status = false; + send! {parent_sender, AppMsg::ToolbxContainerChanged(index, tbx)}; + } + AsyncHandlerMsg::StartToolbx(index, mut tbx) => { + tbx.toolbx_container.start(); + tbx.changing_status = false; + send! {parent_sender, AppMsg::ToolbxContainerChanged(index, tbx)}; + } + } + }); + } + }); + + AsyncHandler { _rt: rt, sender } + } + + fn send(&self, msg: Self::Msg) { + self.sender.blocking_send(msg).unwrap(); + } + + fn sender(&self) -> Self::Sender { + self.sender.clone() + } +} diff --git a/src/ui/components/mod.rs b/src/ui/components/mod.rs index b155c9b..9538ce6 100644 --- a/src/ui/components/mod.rs +++ b/src/ui/components/mod.rs @@ -1,4 +1,5 @@ use relm4::RelmComponent; +use relm4::RelmMsgHandler; use relm4::Sender; use self::{ @@ -6,6 +7,7 @@ use self::{ }; use super::app::model::AppModel; +use super::app::workers::AsyncHandler; pub mod toolbox_apps; pub mod toolbox_settings; @@ -14,4 +16,5 @@ pub mod toolbox_settings; pub struct AppComponents { pub toolbox_settings_dialog: RelmComponent, pub toolbox_apps_dialog: RelmComponent, + pub async_handler: RelmMsgHandler, }