fj_kernel/storage/
handle.rs

1use std::{any::type_name, cmp::Ordering, fmt, hash::Hash, ops::Deref};
2
3use super::{blocks::Index, store::StoreInner};
4
5/// A handle for an object
6///
7/// You can get an instance of `Handle` by inserting an object into a store. A
8/// handle dereferences to the object it points to, via its [`Deref`]
9/// implementation.
10///
11/// # Equality and Identity
12///
13/// Equality of `Handle`s is defined via the objects they reference. If those
14/// objects are equal, the `Handle`s are considered equal.
15///
16/// This is distinct from the *identity* of the referenced objects. Two objects
17/// might be equal, but they might be have been created at different times, for
18/// different reasons, and thus live in different slots in the storage. This is
19/// a relevant distinction when validating objects, as equal but not identical
20/// objects might be a sign of a bug.
21///
22/// You can compare the identity of two objects through their `Handle`s, by
23/// comparing the values returned by [`Handle::id`].
24pub struct Handle<T> {
25    pub(super) store: StoreInner<T>,
26    pub(super) index: Index,
27    pub(super) ptr: *const Option<T>,
28}
29
30impl<T> Handle<T> {
31    /// Access this pointer's unique id
32    pub fn id(&self) -> ObjectId {
33        ObjectId::from_ptr(self.ptr)
34    }
35
36    /// Return a clone of the object this handle refers to
37    pub fn clone_object(&self) -> T
38    where
39        T: Clone,
40    {
41        self.deref().clone()
42    }
43}
44
45impl<T> Deref for Handle<T> {
46    type Target = T;
47
48    fn deref(&self) -> &Self::Target {
49        // `Handle` keeps a reference to `StoreInner`. Since that is an `Arc`
50        // under the hood, we know that as long as an instance of `Handle`
51        // exists, the `StoreInner` its data lives in is still alive. Even if
52        // the `Store` was dropped.
53        //
54        // The `Store` API ensures two things:
55        //
56        // 1. That no `Handle` is ever created, until the object it references
57        //    has at least been reserved.
58        // 2. That the memory objects live in is never deallocated.
59        //
60        // That means that as long as a `Handle` exists, the object it
61        // references has at least been reserved, and has not been deallocated.
62        //
63        // Given all this, we know that the following must be true:
64        //
65        // - The pointer is not null.
66        // - The pointer is properly aligned.
67        // - The pointer is dereferenceable.
68        // - The pointer points to an initialized instance of `T`.
69        //
70        // Further, there is no way to (safely) get a `&mut` reference to any
71        // object in a `Store`/`Block`. So we know that the aliasing rules for
72        // the reference we return here are enforced.
73        //
74        // Furthermore, all of the code mentioned here is covered by unit tests,
75        // which I've run successfully under Miri.
76        let slot = unsafe { &*self.ptr };
77
78        // Can only panic, if the object has been reserved, but the reservation
79        // was never completed.
80        slot.as_ref()
81            .expect("Handle references non-existing object")
82    }
83}
84
85impl<T> Clone for Handle<T> {
86    fn clone(&self) -> Self {
87        Self {
88            store: self.store.clone(),
89            index: self.index,
90            ptr: self.ptr,
91        }
92    }
93}
94
95impl<T> Eq for Handle<T> where T: Eq {}
96
97impl<T> PartialEq for Handle<T>
98where
99    T: PartialEq,
100{
101    fn eq(&self, other: &Self) -> bool {
102        self.deref().eq(other.deref())
103    }
104}
105
106impl<T> Hash for Handle<T>
107where
108    T: Hash,
109{
110    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
111        self.deref().hash(state);
112    }
113}
114
115impl<T> Ord for Handle<T>
116where
117    T: Ord,
118{
119    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
120        self.deref().cmp(other.deref())
121    }
122}
123
124impl<T> PartialOrd for Handle<T>
125where
126    T: PartialOrd,
127{
128    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
129        self.deref().partial_cmp(other.deref())
130    }
131}
132
133impl<T> fmt::Debug for Handle<T>
134where
135    T: fmt::Debug,
136{
137    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        let name = {
139            let type_name = type_name::<T>();
140            match type_name.rsplit_once("::") {
141                Some((_, name)) => name,
142                None => type_name,
143            }
144        };
145        let id = self.id().0;
146        let object = self.deref();
147
148        if f.alternate() {
149            write!(f, "{name} @ {id:#x} => {object:#?}")?;
150        } else {
151            write!(f, "{name} @ {id:#x}")?;
152        }
153
154        Ok(())
155    }
156}
157
158impl<T> From<HandleWrapper<T>> for Handle<T> {
159    fn from(wrapper: HandleWrapper<T>) -> Self {
160        wrapper.0
161    }
162}
163
164unsafe impl<T> Send for Handle<T> {}
165unsafe impl<T> Sync for Handle<T> {}
166
167/// Represents the ID of an object
168///
169/// See [`Handle::id`].
170#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
171pub struct ObjectId(pub(crate) u64);
172
173impl ObjectId {
174    pub(crate) fn from_ptr<T>(ptr: *const T) -> ObjectId {
175        Self(ptr as u64)
176    }
177}
178
179impl fmt::Debug for ObjectId {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let id = self.0;
182        write!(f, "object id {id:#x}")
183    }
184}
185
186/// A wrapper around [`Handle`] to define equality based on identity
187///
188/// This is a utility type that implements [`Eq`]/[`PartialEq`] and other common
189/// traits that are based on those, based on the identity of object that the
190/// wrapped handle references. This is useful, if a type of object doesn't
191/// implement `Eq`/`PartialEq`, which means handles referencing it won't
192/// implement those types either.
193///
194/// Typically, if an object doesn't implement [`Eq`]/[`PartialEq`], it will do
195/// so for good reason. If you need something that represents the object and
196/// implements those missing traits, you might want to be explicit about what
197/// you're doing, and access its ID via [`Handle::id`] instead.
198///
199/// But if you have a struct that owns a [`Handle`] to such an object, and you
200/// want to be able to derive various traits that are not available for the
201/// [`Handle`] itself, this wrapper is for you.
202pub struct HandleWrapper<T>(pub Handle<T>);
203
204impl<T> Deref for HandleWrapper<T> {
205    type Target = Handle<T>;
206
207    fn deref(&self) -> &Self::Target {
208        &self.0
209    }
210}
211
212impl<T> Clone for HandleWrapper<T> {
213    fn clone(&self) -> Self {
214        Self(self.0.clone())
215    }
216}
217
218impl<T> Eq for HandleWrapper<T> {}
219
220impl<T> PartialEq for HandleWrapper<T> {
221    fn eq(&self, other: &Self) -> bool {
222        // The purpose of `HandleWrapper` is to provide equality (and other
223        // traits) for `Handle<T>`s that would otherwise not have them. We use
224        // `Handle::id` to do this. This means, objects that are not identical
225        // are not equal.
226        //
227        // This is desirable for the most part, but it does become horribly
228        // inconvenient in test code. Tests, by design, create equal (but not
229        // identical) objects and compare them against objects produced by the
230        // code under test. Under such a use case, one would rather ignore any
231        // objects wrapped by `HandleWrapper`.
232        //
233        // And the following bit of code does just that. This might be a
234        // horrible hack that will comes back to bite us later (I honestly don't
235        // know), but it is certainly a very economical solution to this
236        // problem.
237        if cfg!(test) {
238            return true;
239        }
240
241        self.0.id().eq(&other.0.id())
242    }
243}
244
245impl<T> Hash for HandleWrapper<T> {
246    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
247        // This piece of code exists to keep this implementation in sync with
248        // the `PartialEq` implementation. See comment there.
249        if cfg!(test) {
250            return;
251        }
252
253        self.0.id().hash(state);
254    }
255}
256
257impl<T> Ord for HandleWrapper<T> {
258    fn cmp(&self, other: &Self) -> Ordering {
259        // This piece of code exists to keep this implementation in sync with
260        // the `PartialEq` implementation. See comment there.
261        if cfg!(test) {
262            return Ordering::Equal;
263        }
264
265        self.0.id().cmp(&other.0.id())
266    }
267}
268
269impl<T> PartialOrd for HandleWrapper<T> {
270    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
271        // This piece of code exists to keep this implementation in sync with
272        // the `PartialEq` implementation. See comment there.
273        if cfg!(test) {
274            return Some(Ordering::Equal);
275        }
276
277        self.0.id().partial_cmp(&other.0.id())
278    }
279}
280
281impl<T> fmt::Debug for HandleWrapper<T>
282where
283    T: fmt::Debug,
284{
285    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286        self.0.fmt(f)
287    }
288}
289
290impl<T> From<Handle<T>> for HandleWrapper<T> {
291    fn from(handle: Handle<T>) -> Self {
292        Self(handle)
293    }
294}
295
296unsafe impl<T> Send for HandleWrapper<T> {}
297unsafe impl<T> Sync for HandleWrapper<T> {}