remote_ref/
lib.rs

1//! This library allows sharing references to objects across thread boundaries,
2//! even when those objects aren't `Send` or `Sync`. The objects themselves are
3//! held in an `ObjectStore` struct that isn't necessarily `Send`/`Sync`, and so
4//! the objects can still only be actually used on the owning thread.
5//!
6//! This differs from some other crates such as
7//! [`fragile`](https://crates.io/crates/fragile) or
8//! [`send_wrapper`](https://crates.io/crates/send_wrapper) in that the access
9//! rule is enforced at compile time, and that the `ObjectStore` (currently)
10//! requires an extra garbage collection function to be called manually.
11
12use std::{
13    marker::PhantomData,
14    sync::{Arc, Weak},
15};
16
17use rich_phantoms::PhantomInvariantAlwaysSendSync;
18use slab::Slab;
19
20/// A reference to an object in an [`ObjectStore`]. This can be held in any
21/// thread, even if `T` isn't `Send` or `Sync`, because in such a case, to
22/// access the object, you'll still need to be on the thread owning the
23/// `ObjectStore`.
24#[must_use]
25#[derive(Clone, Debug)]
26pub struct ObjectRef<T> {
27    index: usize,
28    rc: Arc<PhantomInvariantAlwaysSendSync<T>>,
29}
30
31struct Object<T> {
32    rc: Weak<PhantomInvariantAlwaysSendSync<T>>,
33    data: T,
34}
35
36impl<T> Object<T> {
37    /// Panics if the reference doesn't belong to the same store as `self`.
38    fn verify(&self, obj_ref: &ObjectRef<T>) {
39        assert_eq!(Arc::as_ptr(&obj_ref.rc), Weak::as_ptr(&self.rc));
40    }
41}
42
43/// A storage allowing references to objects that aren't `Send` or `Sync`. The
44/// references ([`ObjectRef`]s) can be held in other threads, even if `T` isn't
45/// `Send` or `Sync`, because in such a case, to access the object, you'll still
46/// need to be on the thread owning the [`ObjectStore`].
47///
48/// `ObjectStore::clean` should be called once in a while to drop any unused
49/// objects, or else [`ObjectStore::remove`] should be called on objects when
50/// dropping them.
51pub struct ObjectStore<T> {
52    slab: Slab<Object<T>>,
53}
54
55impl<T> Default for ObjectStore<T> {
56    fn default() -> Self {
57        Self { slab: Slab::new() }
58    }
59}
60
61impl<T> ObjectStore<T> {
62    /// Returns a reference to the object referred to by `obj_ref`.
63    ///
64    /// # Panics
65    ///
66    /// Panics if the reference doesn't belong to this store.
67    pub fn get(&self, obj_ref: &ObjectRef<T>) -> &T {
68        let obj = &self.slab[obj_ref.index];
69        obj.verify(obj_ref);
70        &obj.data
71    }
72
73    /// Returns a mutable reference to the object referred to by `obj_ref`.
74    ///
75    /// # Panics
76    ///
77    /// Panics if the reference doesn't belong to this store.
78    pub fn get_mut(&mut self, obj_ref: &ObjectRef<T>) -> &mut T {
79        let obj = &mut self.slab[obj_ref.index];
80        obj.verify(obj_ref);
81        &mut obj.data
82    }
83
84    /// Garbage-collects unused objects.
85    pub fn clean(&mut self) {
86        // Note that `slab.retain` makes sure that indexes all stay valid even
87        // when elements are removed, unlike `Vec::retain`.
88        self.slab.retain(|_i, obj| obj.rc.strong_count() > 0)
89    }
90
91    pub fn insert(&mut self, data: T) -> ObjectRef<T> {
92        let rc = Arc::new(PhantomData);
93        let rc_for_return = rc.clone();
94
95        let obj = Object {
96            rc: Arc::downgrade(&rc),
97            data,
98        };
99
100        let index = self.slab.insert(obj);
101
102        ObjectRef {
103            index,
104            rc: rc_for_return,
105        }
106    }
107
108    /// Remove an object reference from the object store. If the reference count
109    /// is then zero, the stored object is dropped and returned. If there are
110    /// still any other active references, None is returned.
111    ///
112    /// # Panics
113    ///
114    /// Panics if the reference doesn't belong to this store.
115    pub fn remove(&mut self, obj_ref: ObjectRef<T>) -> Option<T> {
116        let index = obj_ref.index;
117
118        self.slab[index].verify(&obj_ref);
119
120        if Arc::try_unwrap(obj_ref.rc).is_ok() {
121            // That was the last strong reference - remove the object from the
122            // store.
123            Some(self.slab.remove(index).data)
124        } else {
125            None
126        }
127    }
128}