From b4038e62ec08e9a8ef4cf53676324c4d03ea01bd Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 9 May 2025 22:38:50 +0200 Subject: [PATCH] use an iced stream to update newer_release_available instead of using a mutex --- Cargo.lock | 2 +- Cargo.toml | 5 +-- src/cli/mod.rs | 5 +-- src/gui/components/footer.rs | 8 ++--- src/gui/sniffer.rs | 28 ++++++++-------- src/gui/types/message.rs | 2 ++ src/main.rs | 20 ++---------- src/secondary_threads/check_updates.rs | 44 ++++++++++++++++---------- 8 files changed, 54 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2d5e6ec..f34ac57d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4071,7 +4071,6 @@ dependencies = [ "base64", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", @@ -4715,6 +4714,7 @@ dependencies = [ "serde_test", "serial_test", "splines", + "tokio", "toml 0.8.22", "winres", ] diff --git a/Cargo.toml b/Cargo.toml index 33f5d07a..c1e40f70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,15 +55,16 @@ phf = "0.11.3" phf_shared = "0.11.3" splines = "4.4.2" clap = { version = "4.5.37", features = ["derive"] } +tokio = "1.44.2" [target.'cfg(windows)'.dependencies] gag = "1.0.0" [target.'cfg(not(target_arch = "powerpc64"))'.dependencies] -reqwest = { version = "0.12.15", default-features = false, features = ["json", "blocking", "rustls-tls"] } +reqwest = { version = "0.12.15", default-features = false, features = ["json", "rustls-tls"] } [target.'cfg(target_arch = "powerpc64")'.dependencies] -reqwest = { version = "0.12.15", features = ["json", "blocking"] } +reqwest = { version = "0.12.15", features = ["json"] } #─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 0becde27..6c720082 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -123,9 +123,6 @@ fn test_restore_default_configs() { assert_eq!(Configs::load(), Configs::default()); // only needed because it will delete config files via its Drop implementation - Sniffer::new( - &Arc::new(Mutex::new(Configs::default())), - Arc::new(Mutex::new(Some(true))), - ); + Sniffer::new(&Arc::new(Mutex::new(Configs::default()))); } } diff --git a/src/gui/components/footer.rs b/src/gui/components/footer.rs index dc0c7a1c..983364df 100644 --- a/src/gui/components/footer.rs +++ b/src/gui/components/footer.rs @@ -1,7 +1,5 @@ //! GUI bottom footer -use std::sync::Mutex; - use iced::widget::text::LineHeight; use iced::widget::tooltip::Position; use iced::widget::{Column, Container, Row, Text, Tooltip, button, rich_text, span}; @@ -29,7 +27,7 @@ pub fn footer<'a>( color_gradient: GradientType, font: Font, font_footer: Font, - newer_release_available: &Mutex>, + newer_release_available: Option, ) -> Container<'a, Message, StyleType> { if thumbnail { return thumbnail_footer(); @@ -188,7 +186,7 @@ fn get_release_details<'a>( language: Language, font: Font, font_footer: Font, - newer_release_available: &Mutex>, + newer_release_available: Option, ) -> Row<'a, Message, StyleType> { let mut ret_val = Row::new() .align_y(Alignment::Center) @@ -199,7 +197,7 @@ fn get_release_details<'a>( .size(FONT_SIZE_FOOTER) .font(font_footer), ); - if let Some(boolean_response) = *newer_release_available.lock().unwrap() { + if let Some(boolean_response) = newer_release_available { if boolean_response { // a newer release is available on GitHub let button = button( diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index 336cb0af..6fd98659 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -7,12 +7,13 @@ use std::time::Duration; use iced::Event::{Keyboard, Window}; +use iced::futures::Stream; use iced::keyboard::key::Named; use iced::keyboard::{Event, Key, Modifiers}; use iced::mouse::Event::ButtonPressed; use iced::widget::Column; use iced::window::{Id, Level}; -use iced::{Element, Point, Size, Subscription, Task, window}; +use iced::{Element, Point, Size, Subscription, Task, stream, window}; use pcap::Device; use rfd::FileHandle; @@ -58,6 +59,7 @@ use crate::report::types::report_sort_type::ReportSortType; use crate::report::types::search_parameters::SearchParameters; use crate::report::types::sort_type::SortType; +use crate::secondary_threads::check_updates::set_newer_release_status; use crate::secondary_threads::parse_packets::parse_packets; use crate::translations::types::language::Language; use crate::utils::error_logger::{ErrorLogger, Location}; @@ -82,7 +84,7 @@ pub struct Sniffer { /// Capture data updated by thread parsing packets pub info_traffic: Arc>, /// Reports if a newer release of the software is available on GitHub - pub newer_release_available: Arc>>, + pub newer_release_available: Option, /// Traffic data displayed in GUI pub runtime_data: RunTimeData, /// Network adapter to be analyzed @@ -130,10 +132,7 @@ pub struct Sniffer { } impl Sniffer { - pub fn new( - configs: &Arc>, - newer_release_available: Arc>>, - ) -> Self { + pub fn new(configs: &Arc>) -> Self { let ConfigSettings { style, language, @@ -146,7 +145,7 @@ pub fn new( configs: configs.clone(), current_capture_id: Arc::new(Mutex::new(0)), info_traffic: Arc::new(Mutex::new(InfoTraffic::new())), - newer_release_available, + newer_release_available: None, runtime_data: RunTimeData::new(), device, filters: Filters::default(), @@ -254,6 +253,10 @@ fn window_subscription() -> Subscription { }) } + fn check_updates_stream() -> impl Stream { + stream::channel(1, set_newer_release_status) + } + pub fn update(&mut self, message: Message) -> Task { match message { Message::TickRun => return self.refresh_data(), @@ -536,6 +539,7 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::WindowId(id) => self.id = id, + Message::SetNewerReleaseStatus(status) => self.newer_release_available = status, Message::TickInit => {} } Task::none() @@ -570,7 +574,7 @@ pub fn view(&self) -> Element { color_gradient, font, font_headers, - &self.newer_release_available, + self.newer_release_available, ); let content: Element = @@ -625,6 +629,7 @@ pub fn subscription(&self) -> Subscription { self.mouse_subscription(), self.time_subscription(), Sniffer::window_subscription(), + Subscription::run(Sniffer::check_updates_stream), ]) } @@ -998,15 +1003,12 @@ mod tests { // tests using this will require the #[parallel] annotation fn new_sniffer() -> Sniffer { - Sniffer::new( - &Arc::new(Mutex::new(Configs::default())), - Arc::new(Mutex::new(None)), - ) + Sniffer::new(&Arc::new(Mutex::new(Configs::default()))) } // tests using this will require the #[serial] annotation fn new_sniffer_with_configs(configs: Configs) -> Sniffer { - Sniffer::new(&Arc::new(Mutex::new(configs)), Arc::new(Mutex::new(None))) + Sniffer::new(&Arc::new(Mutex::new(configs))) } // helpful to clean up files generated from tests diff --git a/src/gui/types/message.rs b/src/gui/types/message.rs index ddfe4d68..7e65a3ac 100644 --- a/src/gui/types/message.rs +++ b/src/gui/types/message.rs @@ -126,4 +126,6 @@ pub enum Message { ScaleFactorShortcut(bool), /// Set the window ID WindowId(Option), + /// Set new release status + SetNewerReleaseStatus(Option), } diff --git a/src/main.rs b/src/main.rs index 6c4aeccb..23b9594b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::sync::{Arc, Mutex}; -use std::{panic, process, thread}; +use std::{panic, process}; use iced::advanced::graphics::image::image_rs::ImageFormat; #[cfg(target_os = "linux")] @@ -34,7 +34,6 @@ use crate::configs::types::configs::{CONFIGS, Configs}; use crate::gui::sniffer::FONT_FAMILY_NAME; use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES}; -use crate::secondary_threads::check_updates::set_newer_release_status; use crate::utils::error_logger::{ErrorLogger, Location}; mod chart; @@ -75,9 +74,6 @@ pub fn main() -> iced::Result { let configs1 = Arc::new(Mutex::new(configs)); let configs2 = configs1.clone(); - let newer_release_available1 = Arc::new(Mutex::new(None)); - let newer_release_available2 = newer_release_available1.clone(); - // kill the main thread as soon as a secondary thread panics let orig_hook = panic::take_hook(); panic::set_hook(Box::new(move |panic_info| { @@ -93,13 +89,6 @@ pub fn main() -> iced::Result { }) .log_err(location!()); - let _ = thread::Builder::new() - .name("thread_check_updates".to_string()) - .spawn(move || { - set_newer_release_status(&newer_release_available2); - }) - .log_err(location!()); - print_cli_welcome_message(); let ConfigWindow { size, position, .. } = configs1.lock().unwrap().window; @@ -138,10 +127,5 @@ pub fn main() -> iced::Result { .subscription(Sniffer::subscription) .theme(Sniffer::theme) .scale_factor(Sniffer::scale_factor) - .run_with(move || { - ( - Sniffer::new(&configs1, newer_release_available1), - boot_task_chain, - ) - }) + .run_with(move || (Sniffer::new(&configs1), boot_task_chain)) } diff --git a/src/secondary_threads/check_updates.rs b/src/secondary_threads/check_updates.rs index c5f8b5d5..ea9cfad5 100644 --- a/src/secondary_threads/check_updates.rs +++ b/src/secondary_threads/check_updates.rs @@ -1,11 +1,11 @@ -use std::sync::Mutex; -use std::thread; -use std::time::Duration; - -use serde::Deserialize; - -use crate::SNIFFNET_LOWERCASE; +use crate::gui::types::message::Message; +use crate::utils::error_logger::ErrorLogger; use crate::utils::formatted_strings::APP_VERSION; +use crate::{Location, SNIFFNET_LOWERCASE, location}; +use iced::futures::SinkExt; +use iced::futures::channel::mpsc::Sender; +use serde::Deserialize; +use std::time::Duration; #[derive(Deserialize, Debug)] struct AppVersion { @@ -14,23 +14,27 @@ struct AppVersion { /// Calls a method to check if a newer release of Sniffnet is available on GitHub /// and updates application status accordingly -pub fn set_newer_release_status(newer_release_available: &Mutex>) { - let result = is_newer_release_available(6, 30); - *newer_release_available.lock().unwrap() = result; +pub async fn set_newer_release_status(mut sender: Sender) { + let result = is_newer_release_available(6, 30).await; + let _ = sender + .send(Message::SetNewerReleaseStatus(result)) + .await + .log_err(location!()); } /// Checks if a newer release of Sniffnet is available on GitHub -fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> Option { - let client = reqwest::blocking::Client::new(); +async fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> Option { + let client = reqwest::Client::new(); let response = client .get("https://api.github.com/repos/GyulyVGC/sniffnet/releases/latest") .header("User-agent", format!("{SNIFFNET_LOWERCASE}-{APP_VERSION}")) .header("Accept", "application/vnd.github+json") .header("X-GitHub-Api-Version", "2022-11-28") - .send(); + .send() + .await; if let Ok(result) = response { - let result_json = result.json::(); + let result_json = result.json::().await; #[cfg(test)] if result_json.is_err() { @@ -39,7 +43,8 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O .header("User-agent", format!("{SNIFFNET_LOWERCASE}-{APP_VERSION}")) .header("Accept", "application/vnd.github+json") .header("X-GitHub-Api-Version", "2022-11-28") - .send(); + .send() + .await; println!("\nResponse text: {:?}", response2.unwrap()); println!("JSON result: {result_json:?}\n"); } @@ -52,6 +57,7 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O latest_version = latest_version.trim().to_string(); // release name sample: v1.1.2 + // TODO: support versions with numbers of more than 1 digit let latest_version_as_bytes = latest_version.as_bytes(); if latest_version.len() == 6 && latest_version.starts_with('v') @@ -72,8 +78,12 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O let retries_left = max_retries - 1; if retries_left > 0 { // sleep seconds_between_retries and retries the request - thread::sleep(Duration::from_secs(u64::from(seconds_between_retries))); - is_newer_release_available(retries_left, seconds_between_retries) + tokio::time::sleep(Duration::from_secs(u64::from(seconds_between_retries))).await; + Box::pin(is_newer_release_available( + retries_left, + seconds_between_retries, + )) + .await } else { None }