handles/
lib.rs

1/*
2 * Description: Type-erased strategies for sharing and cloning resources.
3 *
4 * Copyright (C) 2023-2024 Danny McClanahan <dmcC2@hypnicjerk.ai>
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8//! Type-erased strategies for sharing and cloning resources.
9
10#![deny(rustdoc::missing_crate_level_docs)]
11#![deny(missing_docs)]
12/* Make all doctests fail if they produce any warnings. */
13#![doc(test(attr(deny(warnings))))]
14#![deny(clippy::all)]
15#![allow(clippy::collapsible_else_if)]
16#![allow(clippy::result_large_err)]
17
18use std::{any::Any, mem, ptr, rc::Rc, sync::Arc};
19
20/// A mutable wrapper for a dynamic memory allocation which may fail.
21///
22/// The [`Any`] constraint enables downcasting any [`dyn`] object to a concrete
23/// type with e.g. [`dyn Any::downcast_ref()`] or [`Box::downcast()`]. Note that
24/// [`Any`] also requires the `'static` lifetime bound to do this and therefore
25/// types implementing this trait may not hold any non-static reference fields.
26/// This seemed like an appropriate tradeoff for something that is intended to
27/// represent an allocation from some global memory pool.
28///
29/// [`dyn`]: https://doc.rust-lang.org/std/keyword.dyn.html
30pub trait Resource: Any {
31  /// Error type for allocation and deallocation.
32  ///
33  /// This can be [`()`](prim@unit) if the de/allocation are truly infallible.
34  type Error;
35
36  /// Request a new memory allocation, then initialize the allocation with the
37  /// same state as `self`.
38  fn deep_clone(&self) -> Result<Self, Self::Error>
39  where Self: Sized;
40
41  /// Similar to [`Self::deep_clone()`], but places the concrete type in a
42  /// heap-allocated box.
43  ///
44  /// Because of the type erasure in the return value, this method is also
45  /// accessible to `dyn Resource` vtable instances.
46  fn deep_boxed_clone(&self) -> Result<Box<dyn Resource<Error=Self::Error>>, Self::Error>;
47
48  /// Deallocate the memory and release any other resources that were owned by
49  /// this specific object.
50  ///
51  /// This is intended for use with memory pooling smart pointer [`Handle`]s
52  /// which may decide to execute the fallible deallocation method from any of
53  /// the [`Handle`] methods that return [`Result`].
54  ///
55  /// # Safety
56  /// Calling this method more than once is considered a double free, and
57  /// attempting to use the object at all after calling this method is
58  /// considered a use-after-free.
59  unsafe fn sync_drop(&mut self) -> Result<(), Self::Error>;
60}
61
62/// A smart pointer strategy to switch between copy-on-write or copy-up-front
63/// techniques.
64///
65/// As with [`Resource`], this trait requires [`Any`] in order to support
66/// downcasting.
67pub trait Handle: Any {
68  /// The underlying fallible resource type that we want to hand out copies of.
69  ///
70  /// Note that [`Handle`] itself does not define its own error type. The "smart
71  /// pointer" role of [`Handle`] is to perform *infallible* operations like
72  /// reference counting, while letting [`Resource`] cover the clumsy fallible
73  /// initialization work.
74  type R: Resource;
75
76  /// Given an unboxed resource, produce a handle that has exclusive access to
77  /// the resource.
78  fn wrap(r: Self::R) -> Self
79  where Self: Sized;
80
81  /// Given an unboxed handle, create another instance with a strong reference
82  /// to it.
83  fn clone_handle(&self) -> Result<Self, <Self::R as Resource>::Error>
84  where Self: Sized;
85
86  /// Similar to [`Self::clone_handle()`], but places the concrete type in a
87  /// heap-allocated box.
88  fn boxed_clone_handle(&self) -> Result<Box<dyn Handle<R=Self::R>>, <Self::R as Resource>::Error>;
89
90  /// Get a read-only reference to the underlying resource.
91  fn handle_ref(&self) -> &Self::R;
92
93  /// Return whether these two handles point to the same underlying resource
94  /// allocation.
95  fn eq_ref(&self, other: &dyn Handle<R=Self::R>) -> bool { equal_refs(self, other) }
96
97  /// Return [`Some`] if this handle has exclusive access, else [`None`].
98  fn get_mut(&mut self) -> Option<&mut Self::R>;
99
100  /// If there are any other strong or weak references to the same resource,
101  /// disassociate them and clone the resource if necessary.
102  fn ensure_exclusive(&mut self) -> Result<(), <Self::R as Resource>::Error>;
103
104  /// A combination of [`Self::ensure_exclusive()`] and [`Self::get_mut()`].
105  fn make_mut(&mut self) -> Result<&mut Self::R, <Self::R as Resource>::Error> {
106    self.ensure_exclusive()?;
107    Ok(self.get_mut().unwrap())
108  }
109}
110
111/// A free function implementation of [`Handle::eq_ref()`].
112pub fn equal_refs<R: Resource+?Sized>(
113  h1: &(impl Handle<R=R>+?Sized),
114  h2: &(impl Handle<R=R>+?Sized),
115) -> bool {
116  ptr::eq(h1.handle_ref(), h2.handle_ref())
117}
118
119/// A handle can just be the thing itself, which means it's eagerly cloned.
120impl<R: Resource> Handle for R {
121  type R = Self;
122
123  fn wrap(r: Self::R) -> Self
124  where Self: Sized {
125    r
126  }
127
128  fn clone_handle(&self) -> Result<Self, <Self::R as Resource>::Error>
129  where Self: Sized {
130    self.deep_clone()
131  }
132
133  fn boxed_clone_handle(&self) -> Result<Box<dyn Handle<R=Self::R>>, <Self::R as Resource>::Error> {
134    /* TODO: remove the transmute when https://github.com/rust-lang/rust/issues/65991 lands! */
135    let r: Box<dyn Any> = unsafe { mem::transmute(self.deep_boxed_clone()?) };
136    let h: Box<Self> = r.downcast().unwrap();
137    Ok(h)
138  }
139
140  fn handle_ref(&self) -> &Self::R { self }
141
142  fn get_mut(&mut self) -> Option<&mut Self::R> { Some(self) }
143
144  fn ensure_exclusive(&mut self) -> Result<(), <Self::R as Resource>::Error> { Ok(()) }
145}
146
147/// An `Rc` will lazily clone, but can't be sent across threads.
148impl<R: Resource> Handle for Rc<R> {
149  type R = R;
150
151  fn wrap(r: Self::R) -> Self
152  where Self: Sized {
153    Rc::new(r)
154  }
155
156  fn clone_handle(&self) -> Result<Self, <Self::R as Resource>::Error>
157  where Self: Sized {
158    Ok(Rc::clone(self))
159  }
160
161  fn boxed_clone_handle(&self) -> Result<Box<dyn Handle<R=Self::R>>, <Self::R as Resource>::Error> {
162    Ok(Box::new(Rc::clone(self)))
163  }
164
165  fn handle_ref(&self) -> &Self::R { self.as_ref() }
166
167  fn get_mut(&mut self) -> Option<&mut Self::R> { Rc::get_mut(self) }
168
169  fn ensure_exclusive(&mut self) -> Result<(), <Self::R as Resource>::Error> {
170    if Rc::strong_count(self) != 1 {
171      let new: R = self.as_ref().deep_clone()?;
172      *self = Rc::new(new);
173      return Ok(());
174    }
175    if Rc::weak_count(self) > 0 {
176      unsafe {
177        let mut tmp: mem::MaybeUninit<Rc<R>> = mem::MaybeUninit::uninit();
178        let s: *mut Self = self;
179        ptr::swap(s, tmp.as_mut_ptr());
180        /* `self`/`s` is now invalid, and `tmp` is now valid! */
181        /* strong_count == 1 was verified in the if, so this unwrap is safe: */
182        let exclusive = Rc::into_inner(tmp.assume_init()).unwrap_unchecked();
183        /* `tmp` is consumed! */
184        s.write(Rc::new(exclusive));
185        /* `self` is now valid again! */
186      }
187    }
188    Ok(())
189  }
190}
191
192/// An `Arc` will lazily clone and *can* be sent across threads.
193impl<R: Resource> Handle for Arc<R> {
194  type R = R;
195
196  fn wrap(r: Self::R) -> Self
197  where Self: Sized {
198    Arc::new(r)
199  }
200
201  fn clone_handle(&self) -> Result<Self, <Self::R as Resource>::Error>
202  where Self: Sized {
203    Ok(Arc::clone(self))
204  }
205
206  fn boxed_clone_handle(&self) -> Result<Box<dyn Handle<R=Self::R>>, <Self::R as Resource>::Error> {
207    Ok(Box::new(Arc::clone(self)))
208  }
209
210  fn handle_ref(&self) -> &Self::R { self.as_ref() }
211
212  fn get_mut(&mut self) -> Option<&mut Self::R> { Arc::get_mut(self) }
213
214  fn ensure_exclusive(&mut self) -> Result<(), <Self::R as Resource>::Error> {
215    if Arc::strong_count(self) != 1 {
216      let new: R = self.as_ref().deep_clone()?;
217      *self = Arc::new(new);
218      return Ok(());
219    }
220    if Arc::weak_count(self) > 0 {
221      unsafe {
222        let mut tmp: mem::MaybeUninit<Arc<R>> = mem::MaybeUninit::uninit();
223        let s: *mut Self = self;
224        ptr::swap(s, tmp.as_mut_ptr());
225        /* `self`/`s` is now invalid, and `tmp` is now valid! */
226        /* strong_count == 1 was verified in the if, so this unwrap is safe: */
227        let exclusive = Arc::into_inner(tmp.assume_init()).unwrap_unchecked();
228        /* `tmp` is consumed! */
229        s.write(Arc::new(exclusive));
230        /* `self` is now valid again! */
231      }
232    }
233    Ok(())
234  }
235}
236
237#[cfg(test)]
238mod test {
239  use super::*;
240
241  #[derive(Debug, PartialEq, Eq)]
242  struct R {
243    pub state: u8,
244  }
245
246  impl Resource for R {
247    type Error = ();
248    fn deep_clone(&self) -> Result<Self, ()> { Ok(Self { state: self.state }) }
249    fn deep_boxed_clone(&self) -> Result<Box<dyn Resource<Error=()>>, Self::Error> {
250      Ok(Box::new(Self { state: self.state }))
251    }
252    unsafe fn sync_drop(&mut self) -> Result<(), ()> { Ok(()) }
253  }
254
255  #[test]
256  fn test_bare() {
257    let r = R { state: 0 };
258    let mut r1 = r.clone_handle().unwrap();
259
260    /* Because we do not use any wrapper or smart pointer, cloning a "bare
261     * handle" immediately invokes a new allocation. */
262    assert_eq!(r, r1);
263    assert!(!r.eq_ref(&r1));
264
265    r1.ensure_exclusive().unwrap();
266    assert_eq!(r, r1);
267    assert!(!r.eq_ref(&r1));
268
269    r1.make_mut().unwrap().state = 1;
270    assert_ne!(r, r1);
271    assert!(!r.eq_ref(&r1));
272  }
273
274  #[test]
275  fn test_rc() {
276    let r = Rc::new(R { state: 0 });
277    let mut r1 = r.clone_handle().unwrap();
278
279    /* Copy-on-write semantics with lazy cloning. */
280    assert_eq!(r, r1);
281    assert!(r.eq_ref(&r1));
282
283    r1.ensure_exclusive().unwrap();
284    assert_eq!(r, r1);
285    assert!(!r.eq_ref(&r1));
286
287    r1.make_mut().unwrap().state = 1;
288    assert_ne!(r, r1);
289    assert!(!r.eq_ref(&r1));
290  }
291
292  #[test]
293  fn test_arc() {
294    let r = Arc::new(R { state: 0 });
295    let mut r1 = r.clone_handle().unwrap();
296
297    /* Copy-on-write semantics with lazy cloning. */
298    assert_eq!(r, r1);
299    assert!(r.eq_ref(&r1));
300
301    r1.ensure_exclusive().unwrap();
302    assert_eq!(r, r1);
303    assert!(!r.eq_ref(&r1));
304
305    r1.make_mut().unwrap().state = 1;
306    assert_ne!(r, r1);
307    assert!(!r.eq_ref(&r1));
308  }
309
310  #[test]
311  fn test_interchange() {
312    let mut r = Box::new(R { state: 0 });
313    let r1: &mut dyn Handle<R=R> = r.as_mut();
314    let r2 = r1.boxed_clone_handle().unwrap();
315
316    let r2_test1: Box<dyn Any> = r2.boxed_clone_handle().unwrap();
317    let r2_test2: Box<dyn Any> = r2.boxed_clone_handle().unwrap();
318    assert!(r2_test1.downcast::<Rc<R>>().is_err());
319    assert!(r2_test2.downcast::<Arc<R>>().is_err());
320    let r2: Box<dyn Any> = r2;
321    let mut r2: Box<R> = r2.downcast().unwrap();
322
323    assert_eq!(r1.handle_ref(), r2.handle_ref());
324    assert!(!r1.eq_ref(r2.as_ref()));
325    /* Should do nothing, since both are unwrapped ("bare") handles. */
326    r1.ensure_exclusive().unwrap();
327    r2.ensure_exclusive().unwrap();
328
329    let mut r3 = Rc::new(R { state: 0 });
330    let r3: &mut dyn Handle<R=R> = &mut r3;
331    let r4 = r3.boxed_clone_handle().unwrap();
332
333    let r4_test1: Box<dyn Any> = r4.boxed_clone_handle().unwrap();
334    let r4_test2: Box<dyn Any> = r4.boxed_clone_handle().unwrap();
335    assert!(r4_test1.downcast::<R>().is_err());
336    assert!(r4_test2.downcast::<Arc<R>>().is_err());
337    let r4: Box<dyn Any> = r4;
338    let r4: Box<Rc<R>> = r4.downcast().unwrap();
339
340    assert_eq!(r3.handle_ref(), r4.handle_ref());
341    assert_eq!(r3.handle_ref(), r1.handle_ref());
342    assert!(r3.eq_ref(r4.as_ref()));
343    /* Demonstrate that eq_ref() still works for different types of handles. */
344    assert!(!r3.eq_ref(r1));
345
346    r3.ensure_exclusive().unwrap();
347    assert_eq!(r3.handle_ref(), r4.handle_ref());
348    assert!(!r3.eq_ref(r4.as_ref()));
349    r3.get_mut().unwrap().state = 1;
350    assert_ne!(r3.handle_ref(), r4.handle_ref());
351
352    let mut r5 = Arc::new(R { state: 0 });
353    let r5: &mut dyn Handle<R=R> = &mut r5;
354    let r6 = r5.boxed_clone_handle().unwrap();
355
356    let r6_test1: Box<dyn Any> = r6.boxed_clone_handle().unwrap();
357    let r6_test2: Box<dyn Any> = r6.boxed_clone_handle().unwrap();
358    assert!(r6_test1.downcast::<R>().is_err());
359    assert!(r6_test2.downcast::<Rc<R>>().is_err());
360    let r6: Box<dyn Any> = r6;
361    let r6: Box<Arc<R>> = r6.downcast().unwrap();
362
363    assert_eq!(r5.handle_ref(), r6.handle_ref());
364    assert_eq!(r5.handle_ref(), r1.handle_ref());
365    assert!(r5.eq_ref(r6.as_ref()));
366    /* Demonstrate that eq_ref() still compiles for different types of handles. */
367    assert!(!r5.eq_ref(r1));
368    assert!(!r5.eq_ref(r3));
369
370    r5.ensure_exclusive().unwrap();
371    assert_eq!(r5.handle_ref(), r6.handle_ref());
372    assert!(!r5.eq_ref(r6.as_ref()));
373    r5.get_mut().unwrap().state = 1;
374    assert_ne!(r5.handle_ref(), r6.handle_ref());
375  }
376}