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> {}