1use alloc::sync::Arc;
2
3use crate::{
4 id::Id,
5 identity::IdentityManager,
6 lock::{rank, RwLock, RwLockReadGuard},
7 storage::{Element, Storage, StorageItem},
8};
9
10#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
11pub struct RegistryReport {
12 pub num_allocated: usize,
13 pub num_kept_from_user: usize,
14 pub num_released_from_user: usize,
15 pub element_size: usize,
16}
17
18impl RegistryReport {
19 pub fn is_empty(&self) -> bool {
20 self.num_allocated + self.num_kept_from_user == 0
21 }
22}
23
24#[derive(Debug)]
36pub(crate) struct Registry<T: StorageItem> {
37 identity: Arc<IdentityManager<T::Marker>>,
39 storage: RwLock<Storage<T>>,
40}
41
42impl<T: StorageItem> Registry<T> {
43 pub(crate) fn new() -> Self {
44 Self {
45 identity: Arc::new(IdentityManager::new()),
46 storage: RwLock::new(rank::REGISTRY_STORAGE, Storage::new()),
47 }
48 }
49}
50
51#[must_use]
52pub(crate) struct FutureId<'a, T: StorageItem> {
53 id: Id<T::Marker>,
54 data: &'a RwLock<Storage<T>>,
55}
56
57impl<T: StorageItem> FutureId<'_, T> {
58 pub fn assign(self, value: T) -> Id<T::Marker> {
62 let mut data = self.data.write();
63 data.insert(self.id, value);
64 self.id
65 }
66}
67
68impl<T: StorageItem> Registry<T> {
69 pub(crate) fn prepare(&self, id_in: Option<Id<T::Marker>>) -> FutureId<'_, T> {
70 FutureId {
71 id: match id_in {
72 Some(id_in) => {
73 self.identity.mark_as_used(id_in);
74 id_in
75 }
76 None => self.identity.process(),
77 },
78 data: &self.storage,
79 }
80 }
81
82 #[track_caller]
83 pub(crate) fn read<'a>(&'a self) -> RwLockReadGuard<'a, Storage<T>> {
84 self.storage.read()
85 }
86 pub(crate) fn remove(&self, id: Id<T::Marker>) -> T {
87 let value = self.storage.write().remove(id);
88 self.identity.free(id);
92 value
94 }
95
96 pub(crate) fn generate_report(&self) -> RegistryReport {
97 let storage = self.storage.read();
98 let mut report = RegistryReport {
99 element_size: size_of::<T>(),
100 ..Default::default()
101 };
102 report.num_allocated = self.identity.values.lock().count();
103 for element in storage.map.iter() {
104 match *element {
105 Element::Occupied(..) => report.num_kept_from_user += 1,
106 Element::Vacant => report.num_released_from_user += 1,
107 }
108 }
109 report
110 }
111}
112
113impl<T: StorageItem + Clone> Registry<T> {
114 pub(crate) fn get(&self, id: Id<T::Marker>) -> T {
115 self.read().get(id)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::Registry;
122 use crate::{id::Marker, resource::ResourceType, storage::StorageItem};
123 use alloc::sync::Arc;
124
125 struct TestData;
126 struct TestDataId;
127 impl Marker for TestDataId {}
128
129 impl ResourceType for TestData {
130 const TYPE: &'static str = "TestData";
131 }
132 impl StorageItem for TestData {
133 type Marker = TestDataId;
134 }
135
136 #[test]
137 fn simultaneous_registration() {
138 let registry = Registry::new();
139 std::thread::scope(|s| {
140 for _ in 0..5 {
141 s.spawn(|| {
142 for _ in 0..1000 {
143 let value = Arc::new(TestData);
144 let new_id = registry.prepare(None);
145 let id = new_id.assign(value);
146 registry.remove(id);
147 }
148 });
149 }
150 })
151 }
152}