Last active
April 5, 2026 17:24
-
-
Save Ciantic/63526ebfe65570b669eca33bf3404b69 to your computer and use it in GitHub Desktop.
shared app state with static mutex
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [package] | |
| name = "shared-app-state" | |
| version = "0.1.0" | |
| edition = "2024" | |
| [dependencies] | |
| futures = "0.3.32" | |
| rand = "0.10.0" | |
| tokio = { version = "1", features = ["full"] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Now available also as GitHub repo https://github.com/Ciantic/rust-shared-app-state-static-mutex/blob/main/src/main.rs | |
| use futures::{StreamExt, stream}; | |
| use std::sync::{Mutex, OnceLock}; | |
| #[derive(Debug)] | |
| struct AppState { | |
| start_time: std::time::Instant, | |
| end_time: Option<std::time::Instant>, | |
| number_of_dinglebobs: u32, | |
| collected_words: Vec<String>, | |
| } | |
| static APP_STATE: OnceLock<Mutex<AppState>> = OnceLock::new(); | |
| fn state() -> &'static Mutex<AppState> { | |
| APP_STATE.get().expect("AppState not initialized") | |
| } | |
| // Example helper for 'safer' mutation, as the lock is held only during synchronous f call | |
| // | |
| // fn state_mut<R>(f: impl FnOnce(&mut AppState) -> R) -> R { | |
| // let mut s = state().lock().unwrap(); | |
| // f(&mut s) | |
| // } | |
| #[tokio::main] | |
| async fn main() { | |
| APP_STATE.set(Mutex::new(AppState { | |
| start_time: std::time::Instant::now(), | |
| end_time: None, | |
| number_of_dinglebobs: 0, | |
| collected_words: Vec::new(), | |
| })).expect("AppState already initialized"); | |
| // Spawn a render loop that periodically prints the current state | |
| let render_loop = tokio::spawn(async { | |
| loop { | |
| tokio::time::sleep(std::time::Duration::from_millis(100)).await; | |
| let s = state().lock().unwrap(); | |
| eprint!("\rNumber of dinglebobs: {}, all words count: {} ", | |
| s.number_of_dinglebobs, s.collected_words.len()); | |
| if s.end_time.is_some() { | |
| break; | |
| } | |
| } | |
| }); | |
| // Simulate multiple workers doing work and updating the shared state | |
| let _: Vec<_> = stream::iter(0..1000) | |
| .map(|i| { | |
| async move { | |
| let ms = rand::random::<u64>() % 100; | |
| tokio::time::sleep(std::time::Duration::from_millis(ms)).await; | |
| let mut s = state().lock().unwrap(); | |
| s.number_of_dinglebobs += (i % 7) as u32; | |
| s.collected_words.push(format!("word_{}", i)); | |
| // BAD: holding std::sync::MutexGuard across .await | |
| // This blocks the tokio worker thread while sleeping! | |
| // Clippy will warn: `await_holding_lock` | |
| // https://github.com/rust-lang/rust/issues/83310 | |
| // if i == 0 { | |
| // tokio::time::sleep(std::time::Duration::from_secs(1)).await; | |
| // s.number_of_dinglebobs += 999; | |
| // } | |
| } | |
| }) | |
| .buffer_unordered(10) | |
| .collect() | |
| .await; | |
| // Set end time after all workers are done | |
| state().lock().unwrap().end_time = Some(std::time::Instant::now()); | |
| // Wait for the render loop to finish | |
| render_loop.await.unwrap(); | |
| let s = state().lock().unwrap(); | |
| println!("\nDone in {:?}! Total dinglebobs: {}, words collected: {}", | |
| s.end_time.unwrap() - s.start_time, | |
| s.number_of_dinglebobs, | |
| s.collected_words.len()); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment