|
| 1 | +use std::borrow::Borrow; |
| 2 | +use std::hash::{BuildHasher, Hash, Hasher}; |
| 3 | +use std::ops::{Deref, DerefMut}; |
| 4 | + |
| 5 | +use horde::collect::{Pin, pin}; |
| 6 | +pub use horde::sync_table::Read; |
| 7 | +use horde::sync_table::Write; |
| 8 | +use rustc_hash::FxBuildHasher; |
| 9 | + |
| 10 | +use crate::sharded::IntoPointer; |
| 11 | +use crate::sync::{DynSync, Lock, LockGuard}; |
| 12 | + |
| 13 | +pub struct SyncTable<K, V> { |
| 14 | + // We use this lock to protect `table` instead of the internal mutex in `horde::SyncTable` |
| 15 | + // as it's faster when synchronization is disabled. |
| 16 | + lock: Lock<()>, |
| 17 | + |
| 18 | + table: horde::SyncTable<K, V, FxBuildHasher>, |
| 19 | +} |
| 20 | + |
| 21 | +// Memory reclamation can move elements to other threads for dropping, |
| 22 | +// so we require `Sync` instead of `DynSync` here |
| 23 | +unsafe impl<K: Sync, V: Sync> DynSync for SyncTable<K, V> where FxBuildHasher: Sync {} |
| 24 | + |
| 25 | +impl<K, V> Default for SyncTable<K, V> { |
| 26 | + fn default() -> Self { |
| 27 | + Self { lock: Lock::default(), table: horde::SyncTable::default() } |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +impl<K, V> SyncTable<K, V> { |
| 32 | + /// Creates a [Read] handle from a pinned region. |
| 33 | + /// |
| 34 | + /// Use [horde::collect::pin] to get a `Pin` instance. |
| 35 | + #[inline] |
| 36 | + pub fn read<'a>(&'a self, pin: Pin<'a>) -> Read<'a, K, V, FxBuildHasher> { |
| 37 | + self.table.read(pin) |
| 38 | + } |
| 39 | + |
| 40 | + /// Creates a [LockedWrite] handle by taking the underlying mutex that protects writes. |
| 41 | + #[inline] |
| 42 | + pub fn lock(&self) -> LockedWrite<'_, K, V> { |
| 43 | + LockedWrite { |
| 44 | + _guard: self.lock.lock(), |
| 45 | + table: { |
| 46 | + // SAFETY: We ensure there's only 1 writer at a time using our own lock |
| 47 | + unsafe { self.table.unsafe_write() } |
| 48 | + }, |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + /// Hashes a key with the table's hasher. |
| 53 | + #[inline] |
| 54 | + pub fn hash_key<Q>(&self, key: &Q) -> u64 |
| 55 | + where |
| 56 | + K: Borrow<Q>, |
| 57 | + Q: ?Sized + Hash, |
| 58 | + { |
| 59 | + self.table.hash_key::<Q>(key) |
| 60 | + } |
| 61 | + |
| 62 | + pub fn len(&self) -> usize { |
| 63 | + pin(|pin| self.read(pin).len()) |
| 64 | + } |
| 65 | + |
| 66 | + pub fn with_capacity(cap: usize) -> Self { |
| 67 | + Self { lock: Lock::new(()), table: horde::SyncTable::new_with(FxBuildHasher, cap) } |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +/// A handle to a [SyncTable] with write access protected by a lock. |
| 72 | +pub struct LockedWrite<'a, K, V> { |
| 73 | + table: Write<'a, K, V, FxBuildHasher>, |
| 74 | + _guard: LockGuard<'a, ()>, |
| 75 | +} |
| 76 | + |
| 77 | +impl<'a, K, V> Deref for LockedWrite<'a, K, V> { |
| 78 | + type Target = Write<'a, K, V, FxBuildHasher>; |
| 79 | + |
| 80 | + #[inline] |
| 81 | + fn deref(&self) -> &Self::Target { |
| 82 | + &self.table |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +impl<'a, K, V> DerefMut for LockedWrite<'a, K, V> { |
| 87 | + #[inline] |
| 88 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 89 | + &mut self.table |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +impl<K: Eq + Hash + Copy + Send> SyncTable<K, ()> { |
| 94 | + pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool |
| 95 | + where |
| 96 | + K: IntoPointer, |
| 97 | + { |
| 98 | + pin(|pin| { |
| 99 | + let mut state = FxBuildHasher.build_hasher(); |
| 100 | + value.hash(&mut state); |
| 101 | + let hash = state.finish(); |
| 102 | + let value = value.into_pointer(); |
| 103 | + self.read(pin).get_from_hash(hash, |entry| entry.into_pointer() == value).is_some() |
| 104 | + }) |
| 105 | + } |
| 106 | + |
| 107 | + #[inline] |
| 108 | + pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K |
| 109 | + where |
| 110 | + K: Borrow<Q>, |
| 111 | + Q: Hash + Eq, |
| 112 | + { |
| 113 | + pin(|pin| { |
| 114 | + let hash = self.hash_key(value); |
| 115 | + |
| 116 | + let entry = self.read(pin).get(value, Some(hash)); |
| 117 | + if let Some(entry) = entry { |
| 118 | + return *entry.0; |
| 119 | + } |
| 120 | + |
| 121 | + let mut write = self.lock(); |
| 122 | + |
| 123 | + let entry = self.read(pin).get(value, Some(hash)); |
| 124 | + if let Some(entry) = entry { |
| 125 | + return *entry.0; |
| 126 | + } |
| 127 | + |
| 128 | + let result = make(); |
| 129 | + |
| 130 | + write.insert_new(result, (), Some(hash)); |
| 131 | + |
| 132 | + result |
| 133 | + }) |
| 134 | + } |
| 135 | + |
| 136 | + #[inline] |
| 137 | + pub fn intern<Q>(&self, value: Q, make: impl FnOnce(Q) -> K) -> K |
| 138 | + where |
| 139 | + K: Borrow<Q>, |
| 140 | + Q: Hash + Eq, |
| 141 | + { |
| 142 | + pin(|pin| { |
| 143 | + let hash = self.hash_key(&value); |
| 144 | + |
| 145 | + let entry = self.read(pin).get(&value, Some(hash)); |
| 146 | + if let Some(entry) = entry { |
| 147 | + return *entry.0; |
| 148 | + } |
| 149 | + |
| 150 | + let mut write = self.lock(); |
| 151 | + |
| 152 | + let entry = self.read(pin).get(&value, Some(hash)); |
| 153 | + if let Some(entry) = entry { |
| 154 | + return *entry.0; |
| 155 | + } |
| 156 | + |
| 157 | + let result = make(value); |
| 158 | + |
| 159 | + write.insert_new(result, (), Some(hash)); |
| 160 | + |
| 161 | + result |
| 162 | + }) |
| 163 | + } |
| 164 | +} |
0 commit comments