1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
// Take a look at the license at the top of the repository in the LICENSE file.
use std::cell::RefCell;
use std::ops;
// rustdoc-stripper-ignore-next
/// Like `Send` but only if we have the unique reference to the object
///
/// Note that implementing this trait has to be done especially careful.
/// It must only be implemented on types where the uniqueness of a reference
/// can be determined, i.e. the reference count field is accessible, and it
/// must only have references itself to other types that are `Send`.
/// `SendUnique` is *not* enough for the other types unless uniqueness of
/// all of them can be guaranteed, which is e.g. not the case if there's a
/// getter for them.
pub unsafe trait SendUnique: 'static {
fn is_unique(&self) -> bool;
}
// rustdoc-stripper-ignore-next
/// Allows sending reference counted objects that don't implement `Send` to other threads
/// as long as only a single reference to the object exists.
#[derive(Debug)]
pub struct SendUniqueCell<T: SendUnique> {
obj: T,
// Thread id and refcount
thread: RefCell<Option<(usize, usize)>>,
}
unsafe impl<T: SendUnique> Send for SendUniqueCell<T> {}
#[derive(Debug)]
pub struct BorrowError;
impl<T: SendUnique> SendUniqueCell<T> {
// rustdoc-stripper-ignore-next
/// Create a new `SendUniqueCell` out of `obj`
///
/// Fails if `obj` is not unique at this time
pub fn new(obj: T) -> Result<Self, T> {
if !obj.is_unique() {
return Err(obj);
}
Ok(SendUniqueCell {
obj,
thread: RefCell::new(None),
})
}
// rustdoc-stripper-ignore-next
/// Borrow the contained object or panic if borrowing
/// is not possible at this time
pub fn borrow(&self) -> Ref<T> {
#[allow(clippy::match_wild_err_arm)]
match self.try_borrow() {
Err(_) => panic!("Can't borrow"),
Ok(r) => r,
}
}
// rustdoc-stripper-ignore-next
/// Try borrowing the contained object
///
/// Borrowing is possible as long as only a single reference
/// to the object exists, or it is borrowed from the same
/// thread currently
pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError> {
let mut thread = self.thread.borrow_mut();
// If the object is unique, we can borrow it from
// any thread we want and just have to keep track
// how often we borrowed it
if self.obj.is_unique() {
if *thread == None {
*thread = Some((crate::thread_guard::thread_id(), 1));
} else {
thread.as_mut().unwrap().1 += 1;
}
return Ok(Ref(self));
}
// If we don't even know from which thread it is borrowed, this
// means it somehow got borrowed from outside the SendUniqueCell
if *thread == None {
return Err(BorrowError);
}
// If the object is not unique, we can only borrow it
// from the thread that currently has it borrowed
if thread.as_ref().unwrap().0 != crate::thread_guard::thread_id() {
return Err(BorrowError);
}
thread.as_mut().unwrap().1 += 1;
Ok(Ref(self))
}
// rustdoc-stripper-ignore-next
/// Extract the contained object or panic if it is not possible
/// at this time
pub fn into_inner(self) -> T {
#[allow(clippy::match_wild_err_arm)]
match self.try_into_inner() {
Err(_) => panic!("Can't convert into inner type"),
Ok(obj) => obj,
}
}
// rustdoc-stripper-ignore-next
/// Try extracting the contained object
///
/// Borrowing is possible as long as only a single reference
/// to the object exists, or it is borrowed from the same
/// thread currently
pub fn try_into_inner(self) -> Result<T, Self> {
if self.try_borrow().is_err() {
Err(self)
} else {
Ok(self.obj)
}
}
}
pub struct Ref<'a, T: SendUnique>(&'a SendUniqueCell<T>);
impl<'a, T: SendUnique> AsRef<T> for Ref<'a, T> {
fn as_ref(&self) -> &T {
&self.0.obj
}
}
impl<'a, T: SendUnique> ops::Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.0.obj
}
}
impl<'a, T: SendUnique> Drop for Ref<'a, T> {
fn drop(&mut self) {
let is_unique = self.0.obj.is_unique();
let mut thread = self.0.thread.borrow_mut();
if is_unique && thread.as_ref().unwrap().1 == 1 {
*thread = None;
} else {
thread.as_mut().unwrap().1 -= 1;
}
}
}