From 4a17a0a222e98ff63d4acc3ba683f459c7e39d5e Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 18 Jan 2026 22:20:53 -0500 Subject: [PATCH 1/4] chore: update deps and workspace setup --- Cargo.lock | 185 ++++++-------------------------------------- Cargo.toml | 9 ++- nmrs-gui/Cargo.toml | 19 ++--- nmrs/Cargo.toml | 1 + 4 files changed, 44 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 736024e5..96dd9e21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -49,7 +49,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -111,7 +111,7 @@ dependencies = [ "polling", "rustix", "slab", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -169,7 +169,7 @@ dependencies = [ "rustix", "signal-hook-registry", "slab", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -356,7 +356,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -399,7 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -664,7 +664,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -900,16 +900,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -931,17 +921,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mio" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" -dependencies = [ - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", -] - [[package]] name = "nmrs" version = "1.3.5" @@ -971,6 +950,7 @@ dependencies = [ "log", "nmrs", "tokio", + "tokio-util", ] [[package]] @@ -1031,29 +1011,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1094,7 +1051,7 @@ dependencies = [ "hermit-abi", "pin-project-lite", "rustix", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -1130,15 +1087,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "redox_syscall" -version = "0.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_users" version = "0.5.2" @@ -1169,7 +1117,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -1178,12 +1126,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.27" @@ -1267,16 +1209,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "strsim" version = "0.11.1" @@ -1323,7 +1255,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.0", + "windows-sys", ] [[package]] @@ -1352,15 +1284,8 @@ version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", "pin-project-lite", - "signal-hook-registry", - "socket2", "tokio-macros", - "windows-sys 0.61.0", ] [[package]] @@ -1374,6 +1299,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.2" @@ -1622,15 +1560,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.0" @@ -1640,70 +1569,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "winnow" version = "0.5.40" @@ -1756,7 +1621,7 @@ dependencies = [ "tracing", "uds_windows", "uuid", - "windows-sys 0.61.0", + "windows-sys", "winnow 0.7.13", "zbus_macros", "zbus_names", diff --git a/Cargo.toml b/Cargo.toml index c6a9d39c..64e961bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,5 +33,12 @@ uuid = { version = "1.19.0", features = ["v4", "v5"] } futures = "0.3.31" futures-timer = "3.0.3" base64 = "0.22.1" - +nmrs = { path = "nmrs" } +gtk = { version = "0.10.3", package = "gtk4" } +glib = "0.21.5" +dirs = "6.0.0" +fs2 = "0.4.3" +anyhow = "1.0.100" +clap = { version = "4.5.53", features = ["derive"] } tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "sync", "time"] } +tokio-util = { version = "0.7.18" } diff --git a/nmrs-gui/Cargo.toml b/nmrs-gui/Cargo.toml index fcafcd80..ea9429b0 100644 --- a/nmrs-gui/Cargo.toml +++ b/nmrs-gui/Cargo.toml @@ -12,12 +12,13 @@ categories = ["gui"] publish = false [dependencies] -nmrs = { path = "../nmrs", version = "1.1.0" } -gtk = { version = "0.10.3", package = "gtk4" } -glib = "0.21.5" -tokio = { version = "1.48.0", features = ["full"] } -log = "0.4.29" -dirs = "6.0.0" -fs2 = "0.4.3" -anyhow = "1.0.100" -clap = { version = "4.5.53", features = ["derive"] } +nmrs.workspace = true +gtk.workspace = true +glib.workspace = true +tokio.workspace = true +log.workspace = true +dirs.workspace = true +fs2.workspace = true +anyhow.workspace = true +clap.workspace = true +tokio-util.workspace = true diff --git a/nmrs/Cargo.toml b/nmrs/Cargo.toml index 7086f931..e1b3ce6e 100644 --- a/nmrs/Cargo.toml +++ b/nmrs/Cargo.toml @@ -22,6 +22,7 @@ uuid.workspace = true futures.workspace = true futures-timer.workspace = true base64.workspace = true +tokio.workspace = true [dev-dependencies] tokio.workspace = true From daceb104a5541f21064e23d0aad5ec4459643d56 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 18 Jan 2026 22:21:32 -0500 Subject: [PATCH 2/4] fix(#174): clean up signal streams explicitly to ensure unsubscription --- nmrs/src/api/network_manager.rs | 17 +++++++++++++---- nmrs/src/monitoring/device.rs | 26 +++++++++++++++++++++++--- nmrs/src/monitoring/network.rs | 24 ++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/nmrs/src/api/network_manager.rs b/nmrs/src/api/network_manager.rs index 726ae08a..18cd226f 100644 --- a/nmrs/src/api/network_manager.rs +++ b/nmrs/src/api/network_manager.rs @@ -1,3 +1,4 @@ +use tokio::sync::watch; use zbus::Connection; use crate::api::models::{Device, Network, NetworkInfo, WifiSecurity}; @@ -498,11 +499,15 @@ impl NetworkManager { /// # Ok(()) /// # } /// ``` - pub async fn monitor_network_changes(&self, callback: F) -> Result<()> + pub async fn monitor_network_changes( + &self, + shutdown: watch::Receiver<()>, + callback: F, + ) -> Result<()> where F: Fn() + 'static, { - network_monitor::monitor_network_changes(&self.conn, callback).await + network_monitor::monitor_network_changes(&self.conn, shutdown, callback).await } /// Monitors device state changes in real-time. @@ -534,10 +539,14 @@ impl NetworkManager { /// # Ok(()) /// # } /// ``` - pub async fn monitor_device_changes(&self, callback: F) -> Result<()> + pub async fn monitor_device_changes( + &self, + shutdown: watch::Receiver<()>, + callback: F, + ) -> Result<()> where F: Fn() + 'static, { - device_monitor::monitor_device_changes(&self.conn, callback).await + device_monitor::monitor_device_changes(&self.conn, shutdown, callback).await } } diff --git a/nmrs/src/monitoring/device.rs b/nmrs/src/monitoring/device.rs index eeb632eb..8162d9ca 100644 --- a/nmrs/src/monitoring/device.rs +++ b/nmrs/src/monitoring/device.rs @@ -5,8 +5,10 @@ //! to poll. This enables live UI updates for both wired and wireless devices. use futures::stream::{Stream, StreamExt}; -use log::{debug, warn}; +use log::debug; use std::pin::Pin; +use tokio::select; +use tokio::sync::watch; use zbus::Connection; use crate::api::models::ConnectionError; @@ -30,7 +32,11 @@ use crate::Result; /// println!("Device state changed, refresh UI!"); /// }).await?; /// ``` -pub async fn monitor_device_changes(conn: &Connection, callback: F) -> Result<()> +pub async fn monitor_device_changes( + conn: &Connection, + mut shutdown: watch::Receiver<()>, + callback: F, +) -> Result<()> where F: Fn() + 'static, { @@ -74,11 +80,25 @@ where // Merge all streams and listen for any signal let mut merged = futures::stream::select_all(streams); + loop { + select! { + _ = shutdown.changed() => { + debug!("Network monitoring shutdown requested"); + break; + } + signal = merged.next() => { + match signal { + Some(_) => callback(), + None => break, + } + } + } + } + while let Some(_signal) = merged.next().await { debug!("Device change detected"); callback(); } - warn!("Device monitoring stream ended unexpectedly"); Err(ConnectionError::Stuck("monitoring stream ended".into())) } diff --git a/nmrs/src/monitoring/network.rs b/nmrs/src/monitoring/network.rs index 7bce0a18..01575f5e 100644 --- a/nmrs/src/monitoring/network.rs +++ b/nmrs/src/monitoring/network.rs @@ -6,6 +6,8 @@ use futures::stream::{Stream, StreamExt}; use log::{debug, warn}; use std::pin::Pin; +use tokio::select; +use tokio::sync::watch; use zbus::Connection; use crate::api::models::ConnectionError; @@ -30,7 +32,11 @@ use crate::Result; /// println!("Network list changed, refresh UI!"); /// }).await?; /// ``` -pub async fn monitor_network_changes(conn: &Connection, callback: F) -> Result<()> +pub async fn monitor_network_changes( + conn: &Connection, + mut shutdown: watch::Receiver<()>, + callback: F, +) -> Result<()> where F: Fn() + 'static, { @@ -79,11 +85,25 @@ where // Merge all streams and listen for any signal let mut merged = futures::stream::select_all(streams); + loop { + select! { + _ = shutdown.changed() => { + debug!("Network monitoring shutdown requested"); + break; + } + signal = merged.next() => { + match signal { + Some(_) => callback(), + None => break, + } + } + } + } + while let Some(_signal) = merged.next().await { debug!("Network change detected"); callback(); } - warn!("Network monitoring stream ended unexpectedly"); Err(ConnectionError::Stuck("monitoring stream ended".into())) } From 7e08e8daef2709cad85c89836fbf86b743bef75d Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 18 Jan 2026 22:22:01 -0500 Subject: [PATCH 3/4] fix(nmrs-gui): match API for network monitoring state in nmrs-gui --- nmrs-gui/src/ui/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/nmrs-gui/src/ui/mod.rs b/nmrs-gui/src/ui/mod.rs index 43132ded..22f0f3b4 100644 --- a/nmrs-gui/src/ui/mod.rs +++ b/nmrs-gui/src/ui/mod.rs @@ -12,6 +12,7 @@ use gtk::{ }; use std::cell::Cell; use std::rc::Rc; +use tokio::sync::watch; use crate::ui::header::THEMES; @@ -48,6 +49,8 @@ pub fn build_ui(app: &Application) { } } + let (_shutdown_tx, shutdown_rx) = watch::channel(()); + let vbox = GtkBox::new(Orientation::Vertical, 0); let status = Label::new(None); let list_container = GtkBox::new(Orientation::Vertical, 0); @@ -161,6 +164,7 @@ pub fn build_ui(app: &Application) { let is_scanning_device = is_scanning_clone.clone(); let ctx_device = ctx.clone(); let pending_device_refresh = Rc::new(std::cell::RefCell::new(false)); + let shutdown_rx_device = shutdown_rx.clone(); glib::MainContext::default().spawn_local(async move { loop { @@ -168,9 +172,10 @@ pub fn build_ui(app: &Application) { let list_container_clone = list_container_device.clone(); let is_scanning_clone = is_scanning_device.clone(); let pending_device_refresh_clone = pending_device_refresh.clone(); + let shutdown_rx_monitor = shutdown_rx_device.clone(); let result = nm_device_monitor - .monitor_device_changes(move || { + .monitor_device_changes(shutdown_rx_monitor, move || { let ctx = ctx_device_clone.clone(); let list_container = list_container_clone.clone(); let is_scanning = is_scanning_clone.clone(); @@ -214,6 +219,7 @@ pub fn build_ui(app: &Application) { let is_scanning_network = is_scanning_clone.clone(); let ctx_network = ctx.clone(); let pending_network_refresh = Rc::new(std::cell::RefCell::new(false)); + let shutdown_rx_network = shutdown_rx.clone(); glib::MainContext::default().spawn_local(async move { loop { @@ -221,9 +227,10 @@ pub fn build_ui(app: &Application) { let list_container_clone = list_container_network.clone(); let is_scanning_clone = is_scanning_network.clone(); let pending_network_refresh_clone = pending_network_refresh.clone(); + let shutdown_rx_monitor = shutdown_rx_network.clone(); let result = nm_network_monitor - .monitor_network_changes(move || { + .monitor_network_changes(shutdown_rx_monitor, move || { let ctx = ctx_network_clone.clone(); let list_container = list_container_clone.clone(); let is_scanning = is_scanning_clone.clone(); From 1e32843de619e5f5aa3b6eaa732aafa96720063a Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 18 Jan 2026 22:55:11 -0500 Subject: [PATCH 4/4] chore: update CHANGELOG --- nmrs/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/nmrs/CHANGELOG.md b/nmrs/CHANGELOG.md index c2db1231..0a1595c3 100644 --- a/nmrs/CHANGELOG.md +++ b/nmrs/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to the `nmrs` crate will be documented in this file. - Input validation before any D-Bus operations ([#173](https://github.com/cachebag/nmrs/pull/173)) - CI: adjust workflow to auto-update nix hashes on PRs ([#182](https://github.com/cachebag/nmrs/pull/182)) - More helpful methods to `network_manager` facade ([#190](https://github.com/cachebag/nmrs/pull/190)) +- Explicitly clean up signal streams to ensure unsubscription ([#197](https://github.com/cachebag/nmrs/pull/197)) ### Fixed - Race condition in signal subscription ([#191](https://github.com/cachebag/nmrs/pull/191))