1use crate::*;
2use parking_lot::{
3 MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
4};
5use std::cell::Cell;
6use std::convert::TryInto;
7
8thread_local! {
9 static COUNTER: Cell<usize> = const { Cell::new(0) };
10}
11
12pub struct ParObjPool<T, const S: usize> {
25 shards: [RwLock<ObjPool<T>>; S],
26
27 tag: PoolTag,
29}
30
31impl<T, const S: usize> Default for ParObjPool<T, S> {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl<T, const S: usize> ParObjPool<T, S> {
38 const SHARD_BITS: u32 = usize::BITS - (S - 1).leading_zeros();
42
43 const INDEX_BITS: u32 = u32::BITS - Self::SHARD_BITS;
45
46 const INDEX_MASK: u32 = u32::MAX >> Self::SHARD_BITS;
48
49 pub fn new() -> Self {
50 const {
51 assert!(S > 0, "ParObjPool requires at least one shard");
52 assert!(
53 Self::SHARD_BITS < u32::BITS,
54 "too many shards: no bits left in ObjId for the object index"
55 );
56 }
57 let mut shards = Vec::with_capacity(S);
58 shards.resize_with(S, || RwLock::new(ObjPool::<T>::new()));
59 Self {
60 shards: shards.try_into().expect("invalid array"),
61 tag: PoolTag::random(),
62 }
63 }
64
65 pub fn insert(&self, object: T) -> ObjId {
66 let counter = COUNTER.with(|c| {
67 let v = c.get();
68 c.set(v.wrapping_add(1));
69 v
70 });
71 let shard_index = counter % S;
72 let mut shard = self.shards[shard_index].write();
73 let inner = shard.insert(object);
74 let index = shard.obj_id_to_index(inner);
75 drop(shard);
76 self.obj_id_to_external(shard_index, index)
77 }
78
79 pub fn remove(&self, obj_id: ObjId) -> Option<T> {
80 let (shard_index, index) = self.obj_id_from_external(obj_id)?;
81 let mut shard = self.shards[shard_index].write();
82 let inner = shard.index_to_obj_id(index);
83 shard.remove(inner)
84 }
85
86 pub fn get(&self, obj_id: ObjId) -> Option<MappedRwLockReadGuard<'_, T>> {
87 let (shard_index, index) = self.obj_id_from_external(obj_id)?;
88 RwLockReadGuard::try_map(self.shards[shard_index].read(), |obj_pool| {
89 obj_pool.get(obj_pool.index_to_obj_id(index))
90 })
91 .ok()
92 }
93
94 pub fn try_get(&self, obj_id: ObjId) -> Option<MappedRwLockReadGuard<'_, T>> {
95 let (shard_index, index) = self.obj_id_from_external(obj_id)?;
96 RwLockReadGuard::try_map(self.shards[shard_index].try_read()?, |obj_pool| {
97 obj_pool.get(obj_pool.index_to_obj_id(index))
98 })
99 .ok()
100 }
101
102 pub fn get_mut(&self, obj_id: ObjId) -> Option<MappedRwLockWriteGuard<'_, T>> {
103 let (shard_index, index) = self.obj_id_from_external(obj_id)?;
104 RwLockWriteGuard::try_map(self.shards[shard_index].write(), |obj_pool| {
105 obj_pool.get_mut(obj_pool.index_to_obj_id(index))
106 })
107 .ok()
108 }
109
110 pub fn try_get_mut(&self, obj_id: ObjId) -> Option<MappedRwLockWriteGuard<'_, T>> {
111 let (shard_index, index) = self.obj_id_from_external(obj_id)?;
112 RwLockWriteGuard::try_map(self.shards[shard_index].try_write()?, |obj_pool| {
113 obj_pool.get_mut(obj_pool.index_to_obj_id(index))
114 })
115 .ok()
116 }
117
118 pub fn clear(&self) {
119 for shard in &self.shards {
120 shard.write().clear();
121 }
122 }
123
124 pub fn shrink_to_fit(&self) {
125 for shard in &self.shards {
126 let mut shard = shard.write();
127 shard.shrink_to_fit();
128 }
129 }
130
131 pub fn capacity(&self) -> usize {
132 self.shards.iter().map(|s| s.read().capacity()).sum()
133 }
134
135 fn obj_id_to_external(&self, shard_index: usize, index: u32) -> ObjId {
136 debug_assert!(
137 index < Self::INDEX_MASK,
138 "shard is full: object index does not fit into the index bits"
139 );
140 let raw = if Self::SHARD_BITS == 0 {
141 index
142 } else {
143 (shard_index as u32) << Self::INDEX_BITS | index
144 };
145 self.tag.mask_id(ObjId::from_index(raw))
146 }
147
148 fn obj_id_from_external(&self, obj_id: ObjId) -> Option<(usize, u32)> {
149 let raw = self.tag.unmask_id(obj_id).into_index();
150 let (shard_index, index) = if Self::SHARD_BITS == 0 {
151 (0, raw)
152 } else {
153 ((raw >> Self::INDEX_BITS) as usize, raw & Self::INDEX_MASK)
154 };
155 (shard_index < S).then_some((shard_index, index))
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test() {
166 let o = ParObjPool::<usize, 16>::new();
167 let k = o.insert(10);
168 assert_eq!(o.get(k).map(|v| *v), Some(10));
169 let k = o.insert(20);
170 assert_eq!(o.get(k).map(|v| *v), Some(20));
171 let k = o.insert(30);
172 assert_eq!(o.get(k).map(|v| *v), Some(30));
173 assert_eq!(o.try_get(k).map(|v| *v), Some(30));
174 assert_eq!(o.try_get_mut(k).map(|v| *v), Some(30));
175 }
176
177 #[test]
178 fn shard_bits() {
179 assert_eq!(ParObjPool::<u8, 1>::SHARD_BITS, 0);
180 assert_eq!(ParObjPool::<u8, 1>::INDEX_MASK, u32::MAX);
181 assert_eq!(ParObjPool::<u8, 2>::SHARD_BITS, 1);
182 assert_eq!(ParObjPool::<u8, 3>::SHARD_BITS, 2);
183 assert_eq!(ParObjPool::<u8, 16>::SHARD_BITS, 4);
184 assert_eq!(ParObjPool::<u8, 16>::INDEX_MASK, 0x0FFFFFFF);
185 assert_eq!(ParObjPool::<u8, 64>::SHARD_BITS, 6);
186 assert_eq!(ParObjPool::<u8, 64>::INDEX_MASK, 0x03FFFFFF);
187 assert_eq!(ParObjPool::<u8, 65>::SHARD_BITS, 7);
188 assert_eq!(ParObjPool::<u8, 100>::SHARD_BITS, 7);
189 }
190
191 fn insert_get_remove<const S: usize>() {
192 let o = ParObjPool::<usize, S>::new();
193 let keys: Vec<_> = (0..S * 3 + 1).map(|v| (o.insert(v), v)).collect();
195 for (k, v) in &keys {
196 let (shard_index, _) = o.obj_id_from_external(*k).expect("id of this pool");
197 assert!(shard_index < S);
198 assert_eq!(o.get(*k).map(|v| *v), Some(*v));
199 }
200 for (k, v) in &keys {
201 assert_eq!(o.remove(*k), Some(*v));
202 assert_eq!(o.get(*k).map(|v| *v), None);
203 }
204 }
205
206 #[test]
207 fn shard_counts() {
208 insert_get_remove::<1>();
209 insert_get_remove::<2>();
210 insert_get_remove::<3>();
211 insert_get_remove::<16>();
212 insert_get_remove::<64>();
213 insert_get_remove::<100>();
214 }
215
216 #[cfg(debug_assertions)]
217 #[test]
218 fn foreign_ids_are_rejected() {
219 let a = ParObjPool::<usize, 4>::new();
220 let id = a.insert(10);
221
222 let mut b = ParObjPool::<usize, 4>::new();
223 while b.tag.offset == a.tag.offset {
225 b = ParObjPool::new();
226 }
227 let own = b.insert(20);
228
229 assert!(b.get(id).is_none());
230 assert!(b.get_mut(id).is_none());
231 assert!(b.remove(id).is_none());
232 assert_eq!(b.get(own).map(|v| *v), Some(20));
233 }
234
235 #[test]
236 fn clear_releases_and_reuses() {
237 let o = ParObjPool::<usize, 4>::new();
238 let keys: Vec<_> = (0..8).map(|v| o.insert(v)).collect();
239 assert!(o.capacity() >= 8);
240
241 o.clear();
242 assert_eq!(o.capacity(), 0);
243 for k in keys {
244 assert!(o.get(k).is_none());
245 assert!(o.remove(k).is_none());
246 }
247
248 let k = o.insert(42);
249 assert_eq!(o.get(k).map(|v| *v), Some(42));
250 }
251
252 #[test]
253 fn get_mut_mutates_and_remove_twice() {
254 let o = ParObjPool::<usize, 4>::new();
255 let k = o.insert(1);
256
257 *o.get_mut(k).unwrap() = 2;
258 assert_eq!(o.get(k).map(|v| *v), Some(2));
259
260 assert_eq!(o.remove(k), Some(2));
261 assert_eq!(o.remove(k), None);
262 assert!(o.get(k).is_none());
263 assert!(o.get_mut(k).is_none());
264 }
265
266 #[test]
267 fn try_get_respects_shard_write_lock() {
268 let o = ParObjPool::<usize, 2>::new();
269 let k = o.insert(7);
270
271 let guard = o.get_mut(k).unwrap();
272 assert!(o.try_get(k).is_none());
275 assert!(o.try_get_mut(k).is_none());
276 drop(guard);
277
278 assert_eq!(o.try_get(k).map(|v| *v), Some(7));
279 assert_eq!(o.try_get_mut(k).map(|v| *v), Some(7));
280 }
281
282 #[test]
283 fn concurrent_insert_get_remove() {
284 let pool = ParObjPool::<usize, 8>::new();
285 std::thread::scope(|s| {
286 for t in 0..4 {
287 let pool = &pool;
288 s.spawn(move || {
289 let keys: Vec<_> = (0..100)
290 .map(|i| {
291 let value = t * 1000 + i;
292 (pool.insert(value), value)
293 })
294 .collect();
295 for (k, v) in &keys {
296 assert_eq!(pool.get(*k).map(|g| *g), Some(*v));
297 *pool.get_mut(*k).unwrap() += 1;
298 }
299 for (k, v) in keys {
300 assert_eq!(pool.remove(k), Some(v + 1));
301 }
302 });
303 }
304 });
305 }
306}