Skip to content

Instantly share code, notes, and snippets.

@mersinvald
Created November 12, 2018 14:21
Show Gist options
  • Save mersinvald/35958fb17c14b0d827400855d8eb92e8 to your computer and use it in GitHub Desktop.
Save mersinvald/35958fb17c14b0d827400855d8eb92e8 to your computer and use it in GitHub Desktop.
Mutexed singleton example in Rust
use lazy_static::lazy_static;
use std::sync::{RwLock, Mutex};
use std::cell::Cell;
lazy_static! {
static ref SINGLETON: Mutex<Option<Singleton>> = Mutex::new(None);
}
#[derive(Debug, PartialEq)]
struct Singleton {
state: String,
}
impl Singleton {
pub fn initialize(state: String) {
let mut st = SINGLETON.lock().unwrap();
if st.is_none() {
*st = Some(Singleton { state })
} else {
panic!("Singleton is already initialized");
}
}
pub fn get() -> &'static Mutex<Option<Self>> {
if SINGLETON.lock().unwrap().is_some() {
&SINGLETON
} else {
panic!("Singleton must be initialized before use");
}
}
}
#[cfg(test)]
mod tests {
use super::{Singleton, SINGLETON};
use std::sync::Mutex;
use lazy_static::lazy_static;
use std::panic;
use std::sync::atomic::{AtomicUsize, Ordering};
lazy_static! {
// Force tests to run one-at-a-time
static ref TEST_CTR: AtomicUsize = AtomicUsize::new(0);
}
fn run_test(order: usize, body: impl Fn() + panic::RefUnwindSafe) {
// Wait until the order is correct
while TEST_CTR.load(Ordering::Acquire) != order {};
// Remove value from singleton, make it uninit
SINGLETON.lock().unwrap().take();
// Catch panic so we could increment the atomic before crashing test if panic occured
let result = panic::catch_unwind(|| body());
// Let the next test run
TEST_CTR.store(order + 1, Ordering::Release);
// Possibly crash
result.unwrap();
}
#[test]
#[should_panic]
fn use_uninitialized() {
run_test(3, || {
Singleton::get();
});
}
#[test]
#[should_panic]
fn double_initialize() {
run_test(2,|| {
Singleton::initialize(String::from("hello"));
Singleton::initialize(String::from("world"));
});
}
#[test]
fn initialize() {
run_test(1, || {
Singleton::initialize(String::from("hello, world!"));
assert_eq!(
*SINGLETON.lock().unwrap(),
Some(Singleton { state: String::from("hello, world!") })
)
});
}
#[test]
fn initialize_and_get() {
run_test(0, || {
Singleton::initialize(String::from("hello, world!"));
let instance = Singleton::get().lock().unwrap();
assert_eq!(
*instance,
Some(Singleton { state: String::from("hello, world!") })
);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment