Skip to main content

adar_registry/
entry.rs

1use crate::registry::RegistryInterface;
2use std::{
3    marker::PhantomData,
4    mem::MaybeUninit,
5    ops::Deref,
6    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak},
7};
8
9/// Entry index type
10pub type EntryId = u32;
11
12/// Entry controls the lifetime of an entry in the registry. When the entry has its original
13/// type definition, you can also use it to access the stored object. See [`crate::registry::Registry::register()`].
14pub struct Entry<T = ()> {
15    iface: Weak<RwLock<dyn RegistryInterface + 'static>>,
16    id: EntryId,
17    phantom: PhantomData<T>,
18}
19
20impl std::fmt::Debug for Entry {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "E{:?}", self.id)
23    }
24}
25
26impl<T> Entry<T>
27where
28    T: Send + Sync,
29{
30    pub(crate) fn new(iface: Weak<RwLock<dyn RegistryInterface>>, id: EntryId) -> Self {
31        Self {
32            iface,
33            id,
34            phantom: PhantomData,
35        }
36    }
37
38    /// Removes the type definition from the Entry. This method is useful when you want to store different
39    /// kinds of [`Entry`] in one collection.
40    pub fn as_generic(self) -> Entry {
41        let maybe_uninit = MaybeUninit::new(self);
42        let ptr = maybe_uninit.as_ptr();
43        unsafe {
44            // Note: Converting Entry<T> to Entry without calling drop. Drop will be called by type erased Entry later on...
45            Entry {
46                iface: std::ptr::read(&(*ptr).iface),
47                id: std::ptr::read(&(*ptr).id),
48                phantom: PhantomData,
49            }
50        }
51    }
52
53    /// Grants mutable access to the entry. It locks the shared [`RwLock`] of the [`crate::registry::Registry`]. Blocks the current thread until the
54    /// lock can be acquired!
55    /// # Return
56    /// [`None`] if the [`crate::registry::Registry`] no longer exists.
57    pub fn write(&self) -> Option<EntryWriteGuard<T>> {
58        let registry = self.iface.upgrade()?;
59        let ptr = self.iface.as_ptr();
60        // Note: The acquired pointer will be valid as long as a strong reference is alive.
61        // Using a pointer is required because RwLock.write() would partially borrow the registry making it impossible
62        // to create an object containing both a strong pointer and a lock guard.
63        let reference = unsafe { &*ptr };
64        Some(EntryWriteGuard::<T> {
65            _registry: registry,
66            guard: reference.write().unwrap(),
67            entry_id: self.id,
68            phantom: PhantomData,
69        })
70    }
71
72    /// Grants shared read access to the entry. It locks the shared [`RwLock`] of the [`crate::registry::Registry`]. Blocks the current thread until the
73    /// lock can be acquired!
74    /// # Return
75    /// [`None`] if the [`crate::registry::Registry`] no longer exists.
76    pub fn read(&self) -> Option<EntryReadGuard<T>> {
77        let registry = self.iface.upgrade()?;
78        let ptr = self.iface.as_ptr();
79        // Note: The acquired pointer will be valid as long as a strong reference is alive.
80        // Using a pointer is required because RwLock.read() would partially borrow the registry making it impossible
81        // to create an object containing both a strong pointer and a lock guard.
82        let reference = unsafe { &*ptr };
83        Some(EntryReadGuard::<T> {
84            _registry: registry,
85            guard: reference.read().unwrap(),
86            entry_id: self.id,
87            phantom: PhantomData,
88        })
89    }
90
91    /// Gets the underlying id of the entry.
92    pub fn get_id(&self) -> EntryId {
93        self.id
94    }
95
96    /// Leaks the entry. \
97    /// ⚠️ In production environments you should never use this method. It's only meant for quick prototyping or debugging.
98    pub unsafe fn leak(self) {
99        std::mem::forget(self);
100    }
101}
102
103impl<T> Drop for Entry<T> {
104    #[inline(always)]
105    fn drop(&mut self) {
106        if let Some(arc) = self.iface.upgrade() {
107            if let Ok(mut guard) = arc.write() {
108                guard.remove(self.id);
109            }
110        }
111    }
112}
113
114/// Holds a write guard to the entry. See [`Entry::write()`].
115pub struct EntryWriteGuard<'a, T> {
116    _registry: Arc<RwLock<dyn RegistryInterface>>,
117    guard: RwLockWriteGuard<'a, dyn RegistryInterface + 'static>,
118    entry_id: EntryId,
119    phantom: PhantomData<T>,
120}
121
122impl<T: 'static> EntryWriteGuard<'_, T> {
123    /// Acquires a reference to the entry.
124    pub fn get(&self) -> &T {
125        self.guard
126            .get(self.entry_id)
127            .expect("Entry not found in the Registry")
128            .downcast_ref::<T>()
129            .expect("Failed to downcast Entry")
130    }
131
132    /// Acquires a mutable reference to the entry.
133    pub fn get_mut(&mut self) -> &mut T {
134        self.guard
135            .get_mut(self.entry_id)
136            .expect("Entry not found in the Registry")
137            .downcast_mut::<T>()
138            .expect("Failed to downcast Entry")
139    }
140}
141
142/// Holds a read guard to the entry. See [`Entry::read()`].
143pub struct EntryReadGuard<'a, T> {
144    _registry: Arc<RwLock<dyn RegistryInterface>>,
145    guard: RwLockReadGuard<'a, dyn RegistryInterface + 'static>,
146    entry_id: EntryId,
147    phantom: PhantomData<T>,
148}
149
150impl<'a, T: 'static> Deref for EntryReadGuard<'a, T> {
151    type Target = T;
152
153    fn deref(&self) -> &Self::Target {
154        self.guard
155            .get(self.entry_id)
156            .expect("Entry not found in the Registry")
157            .downcast_ref::<T>()
158            .expect("Failed to downcast Entry")
159    }
160}
161
162impl<T: 'static> EntryReadGuard<'_, T> {
163    /// Acquires a reference to the entry.
164    pub fn get(&self) -> &T {
165        self.guard
166            .get(self.entry_id)
167            .expect("Entry not found in the Registry")
168            .downcast_ref::<T>()
169            .expect("Failed to downcast Entry")
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use super::*;
176
177    #[test]
178    fn test() {
179        assert_eq!(size_of::<Entry>(), 24);
180        assert_eq!(size_of::<EntryReadGuard<()>>(), 48);
181    }
182}