use std::{
collections::hash_map::RandomState,
hash::{BuildHasher, Hash},
mem,
num::NonZeroUsize,
ops::Deref,
sync::atomic::{AtomicUsize, Ordering},
};
use hashbrown::hash_map::RawEntryMut;
use crate::{
core::Handle,
loom::cell::UnsafeCell,
loom::sync::Arc,
util::{Alias, BorrowHelper},
view::sealed::ReadAccess,
Map, View,
};
static NEXT_WRITER_UID: AtomicUsize = AtomicUsize::new(1);
#[repr(transparent)]
#[derive(PartialEq, Eq, Clone, Copy)]
struct WriterUid(NonZeroUsize);
impl WriterUid {
#[inline]
fn next() -> Self {
Self(NonZeroUsize::new(NEXT_WRITER_UID.fetch_add(1, Ordering::Relaxed)).unwrap())
}
}
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>>>,
uid: WriterUid,
}
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()),
uid: WriterUid::next(),
}
}
#[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,
handle_uid: self.uid,
})
}
#[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 uid = self.uid;
move |leaked| {
assert!(
uid == leaked.handle_uid,
"Leaked value is not from this map"
);
unsafe { Alias::into_owned(leaked.value) }
}
}
#[inline]
unsafe fn flush_operations(operations: &mut Vec<Operation<K, V>>, map: &mut Map<K, V, S>) {
for Operation {
raw: mut operation,
leaky,
} in operations.drain(..)
{
match operation {
RawOperation::InsertUnique(key, value) => {
map.insert_unique_unchecked(key, value);
}
RawOperation::Replace(ref key, value) => {
let slot =
unsafe { map.get_mut(BorrowHelper::new_ref(key)).unwrap_unchecked() };
if !leaky {
unsafe { Alias::drop(slot) };
}
*slot = value;
}
RawOperation::Remove(ref key) => {
let (mut k, mut v) = unsafe {
map.remove_entry(BorrowHelper::new_ref(key))
.unwrap_unchecked()
};
unsafe { Alias::drop(&mut k) };
if !leaky {
unsafe { Alias::drop(&mut v) };
}
}
RawOperation::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>,
handle_uid: WriterUid,
}
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, K, 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::new(RawOperation::InsertUnique(key, value)));
None
}
RawEntryMut::Occupied(mut entry) => {
let old = mem::replace(entry.get_mut(), unsafe { Alias::copy(&value) });
operations.push(Operation::new(RawOperation::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, K, 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::new(RawOperation::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, K, 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::new(RawOperation::Remove(key)));
}
removed
});
evicted.map(|value| unsafe { Evicted::new(self, value) })
}
#[inline]
pub(crate) fn drop_lazily(&self, leaked: Leaked<V>) {
assert!(
self.handle_uid == leaked.handle_uid,
"Leaked value is not from this map"
);
self.handle.operations.with_mut(|ops_ptr| {
unsafe { &mut *ops_ptr }.push(Operation::new(RawOperation::Drop(Leaked::into_inner(
leaked,
))));
});
}
#[inline]
pub(crate) fn publish(self) {
drop(self);
}
}
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() };
}
}
struct Operation<K, V> {
raw: RawOperation<K, V>,
leaky: bool,
}
impl<K, V> Operation<K, V> {
#[inline]
fn new(raw: RawOperation<K, V>) -> Self {
Self { raw, leaky: false }
}
#[inline]
fn make_leaky(&mut self) {
self.leaky = true;
}
}
enum RawOperation<K, V> {
InsertUnique(Alias<K>, Alias<V>),
Replace(K, Alias<V>),
Remove(K),
Drop(Alias<V>),
}
pub struct Evicted<'a, K, V> {
value: Alias<V>,
operations: &'a UnsafeCell<Vec<Operation<K, V>>>,
operation: usize,
handle_uid: WriterUid,
}
impl<'a, K, V> Evicted<'a, K, V> {
#[inline]
unsafe fn new<S>(guard: &WriteGuard<'a, K, V, S>, value: Alias<V>) -> Self
where
K: Eq + Hash,
S: BuildHasher,
{
let operations = &guard.handle.operations;
let operation = operations.with(|ops_ptr| unsafe { &*ops_ptr }.len() - 1);
Self {
value,
operations,
operation,
handle_uid: guard.handle_uid,
}
}
#[must_use = "Not using a leaked value may cause a memory leak"]
pub fn leak(evicted: Self) -> Leaked<V> {
evicted.operations.with_mut(|ptr| {
unsafe { (&mut *ptr).get_unchecked_mut(evicted.operation) }.make_leaky()
});
Leaked {
value: evicted.value,
handle_uid: evicted.handle_uid,
}
}
}
impl<K, V> Deref for Evicted<'_, K, V> {
type Target = V;
fn deref(&self) -> &Self::Target {
&*self.value
}
}
pub struct Leaked<V> {
value: Alias<V>,
handle_uid: WriterUid,
}
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
}
}