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> = Cell::new(0);
10}
11
12pub struct ParObjPool<T, const S: usize> {
13 shards: [RwLock<ObjPool<T>>; S],
14}
15
16impl<T, const S: usize> ParObjPool<T, S> {
17 pub fn new() -> Self {
18 let mut shards = Vec::with_capacity(S);
19 shards.resize_with(S, || RwLock::new(ObjPool::<T>::new()));
20 Self {
21 shards: shards.try_into().expect("invalid array"),
22 }
23 }
24
25 pub fn insert(&self, object: T) -> ObjId {
26 let counter = COUNTER.with(|c| {
27 let v = c.get();
28 c.set(v.wrapping_add(1));
29 v
30 });
31 let shard_index = counter % S;
32 self.obj_id_to_external(self.shards[shard_index].write().insert(object), shard_index)
33 }
34
35 pub fn remove(&self, obj_id: ObjId) -> Option<T> {
36 let (shard_index, obj_id) = self.obj_id_from_external(obj_id);
37 self.shards[shard_index].write().remove(obj_id)
38 }
39
40 pub fn get(&self, obj_id: ObjId) -> Option<MappedRwLockReadGuard<T>> {
41 let (shard_index, obj_id) = self.obj_id_from_external(obj_id);
42 RwLockReadGuard::try_map(self.shards[shard_index].read(), |obj_pool| {
43 obj_pool.get(obj_id)
44 })
45 .ok()
46 }
47
48 pub fn try_get(&self, obj_id: ObjId) -> Option<MappedRwLockReadGuard<T>> {
49 let (shard_index, obj_id) = self.obj_id_from_external(obj_id);
50 RwLockReadGuard::try_map(if let Some(r) = self.shards[shard_index].try_read() { r } else { return None }, |obj_pool| {
51 obj_pool.get(obj_id)
52 })
53 .ok()
54 }
55
56 pub fn get_mut(&self, obj_id: ObjId) -> Option<MappedRwLockWriteGuard<T>> {
57 let (shard_index, obj_id) = self.obj_id_from_external(obj_id);
58 RwLockWriteGuard::try_map(self.shards[shard_index].write(), |obj_pool| {
59 obj_pool.get_mut(obj_id)
60 })
61 .ok()
62 }
63
64 pub fn try_get_mut(&self, obj_id: ObjId) -> Option<MappedRwLockWriteGuard<T>> {
65 let (shard_index, obj_id) = self.obj_id_from_external(obj_id);
66 RwLockWriteGuard::try_map(if let Some(w) = self.shards[shard_index].try_write() { w } else { return None }, |obj_pool| {
67 obj_pool.get_mut(obj_id)
68 })
69 .ok()
70 }
71
72 pub fn clear(&self) {
73 for shard in &self.shards {
74 let mut shard = shard.write();
75 shard.clear();
76 shard.shrink_to_fit();
77 }
78 }
79
80 pub fn shrink_to_fit(&self) {
81 for shard in &self.shards {
82 let mut shard = shard.write();
83 shard.shrink_to_fit();
84 }
85 }
86
87 pub fn capacity(&self) -> usize {
88 self.shards.iter().map(|s| s.read().capacity()).sum()
89 }
90
91 fn obj_id_to_external(&self, obj_id: ObjId, shard_index: usize) -> ObjId {
92 ObjId(NonZeroU32::new((shard_index << 26) as u32 | obj_id.get()).expect("invalid value"))
93 }
94
95 fn obj_id_from_external(&self, obj_id: ObjId) -> (usize, ObjId) {
96 let v = obj_id.get();
97 (
98 (v >> 26) as usize,
99 ObjId(NonZeroU32::new(v & 0x03FFFFFF).expect("invalid value")),
100 )
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test() {
110 let o = ParObjPool::<usize, 16>::new();
111 let k = o.insert(10);
112 assert_eq!(o.get(k).map(|v| *v), Some(10));
113 let k = o.insert(20);
114 assert_eq!(o.get(k).map(|v| *v), Some(20));
115 let k = o.insert(30);
116 assert_eq!(o.get(k).map(|v| *v), Some(30));
117 assert_eq!(o.try_get(k).map(|v| *v), Some(30));
118 assert_eq!(o.try_get_mut(k).map(|v| *v), Some(30));
119 }
120}