use std::{
collections::hash_map::RandomState,
hash::{BuildHasher, Hash},
marker::PhantomData,
mem,
ops::Deref,
ptr,
};
use hashbrown::hash_map::RawEntryMut;
use crate::{
core::Handle,
loom::cell::UnsafeCell,
loom::sync::Arc,
view::sealed::ReadAccess,
util::{Alias, BorrowHelper},
Map, View,
};
pub struct WriteHandle<K, V, S = RandomState>
where
K: Hash + Eq,
S: BuildHasher,
{
inner: Arc<Handle<K, V, S>>,
operations: UnsafeCell<Vec<Operation<K, V>>>,
}
unsafe impl<K, V, S> Send for WriteHandle<K, V, S>
where
K: Send + Sync + Hash + Eq,
V: Send + Sync,
S: Send + Sync + BuildHasher,
{
}
impl<K, V, S> WriteHandle<K, V, S>
where
K: Hash + Eq,
S: BuildHasher,
{
pub(crate) fn new(inner: Arc<Handle<K, V, S>>) -> Self {
Self {
inner,
operations: UnsafeCell::new(Vec::new()),
}
}
#[inline]
pub fn synchronize(&self) {
self.inner.synchronize();
}
#[inline]
pub fn guard(&mut self) -> View<WriteGuard<'_, K, V, S>> {
self.synchronize();
let map = self.inner.writer_map();
map.with_mut(|map_ptr| {
self.operations.with_mut(|ops_ptr| {
let operations = unsafe { &mut *ops_ptr };
unsafe { Self::flush_operations(operations, &mut *map_ptr) }
operations.shrink_to(64);
});
});
View::new(WriteGuard { map, handle: self })
}
#[inline]
pub fn reclaim_one(&self, leaked: Leaked<V>) -> V {
(self.reclaimer())(leaked)
}
#[inline]
pub fn reclaimer(&self) -> impl Fn(Leaked<V>) -> V + '_ {
self.synchronize();
let source_map = &*self.inner;
move |leaked| {
assert!(ptr::eq(source_map, leaked.source_map.cast()));
unsafe { Alias::into_owned(leaked.value) }
}
}
#[inline]
unsafe fn flush_operations(operations: &mut Vec<Operation<K, V>>, map: &mut Map<K, V, S>) {
for mut operation in operations.drain(..) {
match operation {
Operation::InsertUnique(key, value) => {
map.insert_unique_unchecked(key, value);
}
Operation::Replace(ref key, value) => {
let slot =
unsafe { map.get_mut(BorrowHelper::new_ref(key)).unwrap_unchecked() };
unsafe {
Alias::drop(slot);
}
*slot = value;
}
Operation::ReplaceLeaky(ref key, value) => unsafe {
*map.get_mut(BorrowHelper::new_ref(key)).unwrap_unchecked() = value;
},
Operation::Remove(ref key) => unsafe {
let (mut k, mut v) = map
.remove_entry(BorrowHelper::new_ref(key))
.unwrap_unchecked();
Alias::drop(&mut k);
Alias::drop(&mut v);
},
Operation::RemoveLeaky(ref key) => unsafe {
let (mut k, _v) = map
.remove_entry(BorrowHelper::new_ref(key))
.unwrap_unchecked();
Alias::drop(&mut k);
},
Operation::Drop(ref mut value) => unsafe { Alias::drop(value) },
}
}
}
}
impl<K, V, S> Drop for WriteHandle<K, V, S>
where
K: Hash + Eq,
S: BuildHasher,
{
fn drop(&mut self) {
self.synchronize();
let map = self.inner.writer_map();
map.with_mut(|map_ptr| {
self.operations.with_mut(|ops_ptr| unsafe {
Self::flush_operations(&mut *ops_ptr, &mut *map_ptr)
});
});
}
}
pub struct WriteGuard<'guard, K: Eq + Hash, V, S: BuildHasher> {
map: &'guard UnsafeCell<Map<K, V, S>>,
handle: &'guard WriteHandle<K, V, S>,
}
impl<'guard, K, V, S> ReadAccess for WriteGuard<'guard, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
type Map = Map<K, V, S>;
fn with_map<'read, F, R>(&'read self, op: F) -> R
where
F: FnOnce(&'read Self::Map) -> R,
{
self.map.with(|map_ptr| op(unsafe { &*map_ptr }))
}
}
impl<'guard, K, V, S> WriteGuard<'guard, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
#[inline]
fn with_map_mut<'write, F, R>(&'write mut self, op: F) -> R
where
F: FnOnce(&'write mut Map<K, V, S>, &'write mut Vec<Operation<K, V>>) -> R,
{
self.map.with_mut(|map_ptr| {
self.handle
.operations
.with_mut(|ops_ptr| unsafe { op(&mut *map_ptr, &mut *ops_ptr) })
})
}
#[inline]
pub(crate) fn insert<'ret>(&mut self, key: K, value: V) -> Option<Evicted<'ret, V>>
where
'guard: 'ret,
{
let value = Alias::new(value);
let evicted = self.with_map_mut(|map, operations| {
match map.raw_entry_mut().from_key(BorrowHelper::new_ref(&key)) {
RawEntryMut::Vacant(entry) => {
let key = Alias::new(key);
entry.insert(unsafe { Alias::copy(&key) }, unsafe { Alias::copy(&value) });
operations.push(Operation::InsertUnique(key, value));
None
}
RawEntryMut::Occupied(mut entry) => {
let old = mem::replace(entry.get_mut(), unsafe { Alias::copy(&value) });
operations.push(Operation::Replace(key, value));
Some(old)
}
}
});
evicted.map(|alias| unsafe { Evicted::new(self, alias) })
}
#[inline]
pub(crate) fn replace<'ret, F>(&mut self, key: K, op: F) -> Option<Evicted<'ret, V>>
where
F: FnOnce(&V) -> V,
'guard: 'ret,
{
let evicted =
self.with_map_mut(
|map, operations| match map.get_mut(BorrowHelper::new_ref(&key)) {
Some(value) => {
let new_value = Alias::new(op(&**value));
operations
.push(Operation::Replace(key, unsafe { Alias::copy(&new_value) }));
let old_value = mem::replace(value, new_value);
Some(old_value)
}
None => None,
},
);
evicted.map(|value| unsafe { Evicted::new(self, value) })
}
#[inline]
pub(crate) fn remove<'ret>(&mut self, key: K) -> Option<Evicted<'ret, V>>
where
'guard: 'ret,
{
let evicted = self.with_map_mut(|map, operations| {
let removed = map.remove(BorrowHelper::new_ref(&key));
if removed.is_some() {
operations.push(Operation::Remove(key));
}
removed
});
evicted.map(|value| unsafe { Evicted::new(self, value) })
}
#[inline]
pub(crate) fn drop_lazily(&self, leaked: Leaked<V>) {
assert!(ptr::eq(&*self.handle.inner, leaked.source_map.cast()));
self.handle.operations.with_mut(|ops_ptr| {
unsafe { &mut *ops_ptr }.push(Operation::Drop(Leaked::into_inner(leaked)));
});
}
}
impl<'guard, K, V, S> Drop for WriteGuard<'guard, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn drop(&mut self) {
unsafe { self.handle.inner.finish_write() };
}
}
enum Operation<K, V> {
InsertUnique(Alias<K>, Alias<V>),
Replace(K, Alias<V>),
ReplaceLeaky(K, Alias<V>),
Remove(K),
RemoveLeaky(K),
Drop(Alias<V>),
}
impl<K, V> Operation<K, V> {
fn make_leaky(&mut self) {
let old = unsafe { ptr::read(self) };
let new = match old {
Self::Replace(key, value) => Self::ReplaceLeaky(key, value),
Self::Remove(key) => Self::RemoveLeaky(key),
op => op,
};
unsafe {
ptr::write(self, new);
}
}
}
pub struct Evicted<'a, V> {
value: Alias<V>,
handle: *const (),
operation: usize,
leak: unsafe fn(*const (), usize) -> *const (),
_lifetime: PhantomData<&'a ()>,
}
impl<'a, V> Evicted<'a, V> {
#[inline]
unsafe fn new<K, S>(guard: &WriteGuard<'a, K, V, S>, value: Alias<V>) -> Self
where
K: Eq + Hash,
S: BuildHasher,
{
let handle: *const () = (&*guard.handle as *const WriteHandle<K, V, S>).cast();
let operation = guard
.handle
.operations
.with(|ops_ptr| unsafe { &*ops_ptr }.len() - 1);
let leak = |write_handle: *const (), op: usize| {
let write_handle_ref: &WriteHandle<K, V, S> = unsafe { &*write_handle.cast() };
write_handle_ref.operations.with_mut(|ops_ptr| {
unsafe { (&mut *ops_ptr).get_mut(op).unwrap_unchecked() }.make_leaky();
});
(&*write_handle_ref.inner as *const Handle<K, V, S>).cast()
};
Self {
value,
handle,
operation,
leak,
_lifetime: PhantomData,
}
}
#[must_use = "Not using a leaked value may cause a memory leak"]
pub fn leak(evicted: Self) -> Leaked<V> {
let source_map = unsafe { (evicted.leak)(evicted.handle, evicted.operation) };
Leaked {
value: evicted.value,
source_map,
}
}
}
impl<V> Deref for Evicted<'_, V> {
type Target = V;
fn deref(&self) -> &Self::Target {
&*self.value
}
}
pub struct Leaked<V> {
value: Alias<V>,
source_map: *const (),
}
unsafe impl<V> Send for Leaked<V> where Alias<V>: Send {}
unsafe impl<V> Sync for Leaked<V> where Alias<V>: Sync {}
impl<V> Leaked<V> {
#[must_use = "Not using an aliased value may cause a memory leak"]
pub fn into_inner(leaked: Self) -> Alias<V> {
leaked.value
}
}
impl<V> Deref for Leaked<V> {
type Target = V;
fn deref(&self) -> &Self::Target {
&*self.value
}
}