Skip to content

Instantly share code, notes, and snippets.

@Ciantic
Last active April 5, 2026 17:24
Show Gist options
  • Select an option

  • Save Ciantic/63526ebfe65570b669eca33bf3404b69 to your computer and use it in GitHub Desktop.

Select an option

Save Ciantic/63526ebfe65570b669eca33bf3404b69 to your computer and use it in GitHub Desktop.
shared app state with static mutex
[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"] }
// 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