blob: a81b3fb8d27f069371b24c055b82bdb012859177 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use parking_lot::RwLock;
use std::{
borrow::Borrow,
collections::{hash_map::Entry, HashMap},
hash::Hash,
sync::{Arc, Weak},
};
/// A weak reference to an entry in a DetachableMap. This is a weak reference, if the entry is
/// detached before it is upgraded, the entry can be gone.
pub struct DetachableWeak<K, V> {
inner: Weak<V>,
parent: Arc<RwLock<HashMap<K, Arc<V>>>>,
key: K,
}
impl<K: Clone, V> Clone for DetachableWeak<K, V> {
fn clone(&self) -> Self {
Self { inner: self.inner.clone(), parent: self.parent.clone(), key: self.key.clone() }
}
}
impl<K: Hash + Eq, V> DetachableWeak<K, V> {
fn new(item: &Arc<V>, parent: Arc<RwLock<HashMap<K, Arc<V>>>>, key: K) -> Self {
Self { inner: Arc::downgrade(item), parent, key }
}
/// Attempt to upgrade the weak pointer to an Arc, extending the lifetime of the value if
/// successful. Returns None if the item has been dropped (by another client detaching this)
pub fn upgrade(&self) -> Option<Arc<V>> {
self.inner.upgrade()
}
/// Destroys the original reference to this vended item.
/// If other references to the item exist (from `upgrade`), it will not be dropped until those
/// references are dropped.
pub fn detach(self) {
self.parent.write().remove(&self.key);
}
/// Get a reference to the key for this entry.
pub fn key(&self) -> &K {
&self.key
}
}
pub struct LazyEntry<K, V> {
parent: Arc<RwLock<HashMap<K, Arc<V>>>>,
key: K,
}
impl<K: Clone, V> Clone for LazyEntry<K, V> {
fn clone(&self) -> Self {
Self { parent: self.parent.clone(), key: self.key.clone() }
}
}
impl<K: Hash + Eq + Clone, V> LazyEntry<K, V> {
fn new(parent: Arc<RwLock<HashMap<K, Arc<V>>>>, key: K) -> Self {
Self { parent, key }
}
/// Get a reference to the key that this entry is built for.
pub fn key(&self) -> &K {
&self.key
}
/// Attempt to insert into the map at `key`. Returns a detachable weak entry if the value was
/// inserted, and Err(value) if the item already existed.
/// Err(value) if the value was not able to be inserted.
pub fn try_insert(&self, value: V) -> Result<DetachableWeak<K, V>, V> {
match self.parent.write().entry(self.key.clone()) {
Entry::Occupied(_) => return Err(value),
Entry::Vacant(entry) => entry.insert(Arc::new(value)),
};
Ok(self.get().unwrap())
}
/// Attempt to resolve the entry to a weak reference, as returned by `DetachableMap::get`.
/// Returns None if the key does not exist.
pub fn get(&self) -> Option<DetachableWeak<K, V>> {
self.parent.read().get(&self.key).and_then(|v| {
let map = self.parent.clone();
let key = self.key.clone();
Some(DetachableWeak::new(&v, map, key))
})
}
}
/// A Map with detachable entries. After retrieval, entries can be "detached", removing them from
/// the map, allowing any client to expire the key in the map. They are weak, and can be upgraded
/// to a strong reference to the stored object.
pub struct DetachableMap<K, V> {
map: Arc<RwLock<HashMap<K, Arc<V>>>>,
}
impl<K: Hash + Eq + Clone, V> Default for DetachableMap<K, V> {
fn default() -> Self {
Self { map: Arc::new(RwLock::new(HashMap::new())) }
}
}
impl<K: Hash + Eq + Clone, V> DetachableMap<K, V> {
/// Creates an empty `DetachableMap`. The map is initially empty.
pub fn new() -> DetachableMap<K, V> {
Default::default()
}
/// Inserts a new item into the map at `key`
/// Returns a reference to the old item at `key` if one existed or None otherwise.
pub fn insert(&mut self, key: K, value: V) -> Option<Arc<V>> {
self.map.write().insert(key, Arc::new(value))
}
/// True if the map contains a value for the specified key The key may be any borrowed form of
/// the key's type, with `Hash` and `Eq` matching the type.
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.map.read().contains_key(key)
}
/// Returns a detachable reference to the value at the given key, if it exists.
pub fn get(&self, key: &K) -> Option<DetachableWeak<K, V>> {
self.map.read().get(key).and_then(|v| {
let map = self.map.clone();
let key = key.clone();
Some(DetachableWeak::new(&v, map, key))
})
}
/// Returns a lazy entry. Lazy Entries can be used later to attempt to insert into the map if
/// the key doesn't exist.
/// They can also be resolved to a detachable reference (as returned by `DetachableMap::get`) if
/// the key already exists.
pub fn lazy_entry(&self, key: &K) -> LazyEntry<K, V> {
LazyEntry::new(self.map.clone(), key.clone())
}
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Default, Debug)]
struct TestStruct {
data: u32,
}
#[test]
fn contains_keys() {
let mut map = DetachableMap::default();
map.insert(0, TestStruct { data: 45 });
assert!(map.contains_key(&0));
let detached = map.get(&0);
assert!(detached.is_some());
// Detaching removes it from the map.
detached.unwrap().detach();
assert_eq!(false, map.contains_key(&0));
}
#[test]
fn upgrade_detached() {
let mut map = DetachableMap::default();
map.insert(0, TestStruct { data: 45 });
let detached = map.get(&0);
assert!(detached.is_some());
let detached = detached.unwrap();
let detached_clone = detached.clone();
let second = map.get(&0);
assert!(second.is_some());
let second = second.unwrap();
let upgraded = detached.upgrade().expect("should be able to upgrade");
// Detaching should mean we can't get it from the map anymore (and consumes second)
second.detach();
assert!(map.get(&0).is_none());
// We can still upgrade because it's still around from the strong ref.
let second_up = detached.upgrade().expect("should be able to upgrade");
let third_up = detached_clone.upgrade().expect("should be able to upgrade");
// Dropping all the strong refs means neither can upgrade anymore though.
drop(upgraded);
drop(second_up);
drop(third_up);
assert!(detached.upgrade().is_none());
// Detaching twice doesn't do anything (and doesn't panic)
detached.detach();
}
#[test]
fn lazy_entry() {
let map = DetachableMap::default();
// Should be able to get an entry before the key exists.
let entry = map.lazy_entry(&1);
// Can't get a reference if the key doesn't exist.
assert!(entry.get().is_none());
// We can insert though.
let detachable =
entry.try_insert(TestStruct { data: 45 }).expect("should be able to insert");
// Can't insert if there's something there though.
let second_val = TestStruct { data: 56 };
let returned_val =
entry.try_insert(second_val).err().expect("should get an error when trying to insert");
assert_eq!(56, returned_val.data);
assert!(entry.get().is_some());
// If we detach though, the entry is empty again, and we can insert again.
detachable.detach();
assert!(entry.get().is_none());
let new = entry.try_insert(returned_val).expect("should be able to insert after removal");
// Deopping the new entry doesn't remove it from the map.
drop(new);
let still_there = map.get(&1).expect("should be there");
assert!(still_there.upgrade().is_some());
}
}