Created
November 12, 2018 14:21
-
-
Save mersinvald/35958fb17c14b0d827400855d8eb92e8 to your computer and use it in GitHub Desktop.
Mutexed singleton example in Rust
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
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