Skip to content

Instantly share code, notes, and snippets.

@jpastuszek
Forked from rust-play/playground.rs
Last active May 12, 2022 02:30
Show Gist options
  • Save jpastuszek/80ed6a8929ec93259c51f5ee94a6e8ec to your computer and use it in GitHub Desktop.
Save jpastuszek/80ed6a8929ec93259c51f5ee94a6e8ec to your computer and use it in GitHub Desktop.
Rust: RwLock Cache
use std::sync::Mutex;
use std::sync::RwLock;
use std::sync::Arc;
use std::collections::HashMap;
use lazy_static::lazy_static;
lazy_static! {
static ref CACHE: Mutex<HashMap<&'static str, Arc<RwLock<Option<u32>>>>> = Mutex::new(HashMap::new());
}
fn get(key: &'static str) -> u32 {
// take global lock
let mut c = CACHE.try_lock().or_else(|_| {
println!("[{:?}] {}: wait cache", std::thread::current().id(), key);
CACHE.lock()
}).unwrap();
if let Some(e) = c.get(key) {
let entry_guard = e.clone();
// unlock global before taking entry lock so other threads can also get the entry lock
drop(c);
// block if value still is beign produced (entry_guard write lock)
let v = entry_guard.try_read().or_else(|_| {
println!("[{:?}] {}: wait entry read", std::thread::current().id(), key);
entry_guard.read()
}).unwrap().as_ref().unwrap().clone();
println!("[{:?}] {}: hit: {}", std::thread::current().id(), key, v);
return v
} else {
c.insert(key, Arc::new(RwLock::new(None))); // put stub value
let entry_guard = c.get(key).unwrap().clone();
// lock entry before releasing global lock so stub value cannot be observed
let mut entry = entry_guard.try_write().or_else(|_| {
println!("[{:?}] {}: wait entry write", std::thread::current().id(), key);
entry_guard.write()
}).unwrap();
// unlock global
drop(c);
println!("[{:?}] {}: miss, producing", std::thread::current().id(), key);
std::thread::sleep(std::time::Duration::from_secs(2));
let v = 1;
*entry = Some(v.clone());
println!("[{:?}] {}: cached: {}", std::thread::current().id(), key, v);
return v
}
}
fn main() {
let t1 = std::thread::spawn(|| {
for s in &["foo", "bar", "baz"] {
dbg![get(s)];
dbg![get(s)];
}
});
let t2 = std::thread::spawn(|| {
for s in &["foo", "bar", "baz"] {
dbg![get(s)];
dbg![get(s)];
}
});
t1.join().unwrap();
t2.join().unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment