Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- mod cache {
- use std::{cell::UnsafeCell, mem, sync::Mutex};
- /// A thread safe single-value cache.
- pub struct ThreadSafeCache<T> {
- data: Mutex<UnsafeCell<Option<T>>>,
- }
- impl<T> ThreadSafeCache<T> {
- /// Create a new instance of the cache with uninitialized data.
- pub fn new() -> Self {
- Self {
- data: Mutex::new(UnsafeCell::new(None)),
- }
- }
- /// Get the cached data, or calculate it using the given closure if it
- /// hasn't been initialized.
- ///
- /// # Panics
- ///
- /// Panics if the cache's mutex has been poisoned.
- pub fn get<F>(&self, f: F) -> &T
- where
- F: FnOnce() -> T,
- {
- // Get a lock on data. We get this to prevent double-initialization
- // of the cached data, as well as other issues with potentially
- // having multiple mutable references. While we have the lock, we
- // can create shared references with impunity, since the only block
- // of code that should be allowed to get one is this method.
- let cell = self.data.lock().unwrap();
- let ptr = cell.get();
- unsafe {
- // This can't be a mutable reference - we don't know if the data
- // has been initialized yet, and if it has, there are
- // potentially shared references to its contents. We have to
- // limit its lifetime to this expression, as well, since we will
- // need to get a shared reference if we need to initialize.
- if (&*ptr).is_none() {
- // We can get a mutable reference here since we know that
- // there aren't any other mutable references (due to the
- // mutex), there aren't any external shared references
- // (since we can only get a reference to the data after it's
- // been initialized,) and we've prevented any local shared
- // references by limiting the scope of the `is_none` check.
- let data = &mut *ptr;
- mem::replace(data, Some(f()));
- }
- // Safe to get a reference because the mutable reference's
- // lifetime extends only to the end of the `if` block.
- (&*ptr).as_ref().unwrap()
- }
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn cache_is_send_and_sync() {
- fn is_send_and_sync<T: Send + Sync>() {}
- is_send_and_sync::<ThreadSafeCache<u32>>()
- }
- }
- }
- use self::cache::ThreadSafeCache;
- struct Foo {
- name: String,
- msg: ThreadSafeCache<String>,
- }
- impl Foo {
- fn new<T: Into<String>>(name: T) -> Self {
- Self {
- name: name.into(),
- msg: ThreadSafeCache::new(),
- }
- }
- fn msg(&self) -> &str {
- self.msg.get(|| {
- println!("Long computation here...");
- format!("Hello, {}!", self.name)
- })
- }
- }
- fn main() {
- let x = Foo::new("world");
- println!("{}", x.msg());
- println!("{}", x.msg());
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement