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}