/// A [`SemiSticky<T>`] keeps a value T stored in a thread if it has a drop. /// /// This is a combined version of [`Fragile`] and [`Sticky`]. If the type /// does not have a drop it will effectively be a [`Fragile`], otherwise it /// will be internally behave like a [`Sticky`]. /// /// This type requires `T: 'static` for the same reasons as [`Sticky`] and /// also uses [`StackToken`]s. pubstruct SemiSticky<T: 'static> {
inner: SemiStickyImpl<T>,
}
impl<T> SemiSticky<T> { /// Creates a new [`SemiSticky`] wrapping a `value`. /// /// The value that is moved into the `SemiSticky` can be non `Send` and /// will be anchored to the thread that created the object. If the /// sticky wrapper type ends up being send from thread to thread /// only the original thread can interact with the value. In case the /// value does not have `Drop` it will be stored in the [`Fragile`] /// instead. pubfn new(value: T) -> Self {
SemiSticky {
inner: if mem::needs_drop::<T>() {
SemiStickyImpl::Sticky(Sticky::new(value))
} else {
SemiStickyImpl::Fragile(Box::new(Fragile::new(value)))
},
}
}
/// Returns `true` if the access is valid. /// /// This will be `false` if the value was sent to another thread. pubfn is_valid(&self) -> bool { matchself.inner {
SemiStickyImpl::Fragile(ref inner) => inner.is_valid(),
SemiStickyImpl::Sticky(ref inner) => inner.is_valid(),
}
}
/// Consumes the [`SemiSticky`], returning the wrapped value. /// /// # Panics /// /// Panics if called from a different thread than the one where the /// original value was created. pubfn into_inner(self) -> T { matchself.inner {
SemiStickyImpl::Fragile(inner) => inner.into_inner(),
SemiStickyImpl::Sticky(inner) => inner.into_inner(),
}
}
/// Consumes the [`SemiSticky`], returning the wrapped value if successful. /// /// The wrapped value is returned if this is called from the same thread /// as the one where the original value was created, otherwise the /// [`SemiSticky`] is returned as `Err(self)`. pubfn try_into_inner(self) -> Result<T, Self> { matchself.inner {
SemiStickyImpl::Fragile(inner) => inner.try_into_inner().map_err(|inner| SemiSticky {
inner: SemiStickyImpl::Fragile(Box::new(inner)),
}),
SemiStickyImpl::Sticky(inner) => inner.try_into_inner().map_err(|inner| SemiSticky {
inner: SemiStickyImpl::Sticky(inner),
}),
}
}
/// Immutably borrows the wrapped value. /// /// # Panics /// /// Panics if the calling thread is not the one that wrapped the value. /// For a non-panicking variant, use [`try_get`](Self::try_get). pubfn get<'stack>(&'stack self, _proof: &'stack StackToken) -> &'</span>stack T { matchself.inner {
SemiStickyImpl::Fragile(ref inner) => inner.get(),
SemiStickyImpl::Sticky(ref inner) => inner.get(_proof),
}
}
/// Mutably borrows the wrapped value. /// /// # Panics /// /// Panics if the calling thread is not the one that wrapped the value. /// For a non-panicking variant, use [`try_get_mut`](Self::try_get_mut). pubfn get_mut<'stack>(&'stack mutself, _proof: &'stack StackToken) -> &'stack mut T { matchself.inner {
SemiStickyImpl::Fragile(refmut inner) => inner.get_mut(),
SemiStickyImpl::Sticky(refmut inner) => inner.get_mut(_proof),
}
}
/// Tries to immutably borrow the wrapped value. /// /// Returns `None` if the calling thread is not the one that wrapped the value. pubfn try_get<'stack>(
&'stack self,
_proof: &'stack StackToken,
) -> Result<&'stack T, InvalidThreadAccess> { matchself.inner {
SemiStickyImpl::Fragile(ref inner) => inner.try_get(),
SemiStickyImpl::Sticky(ref inner) => inner.try_get(_proof),
}
}
/// Tries to mutably borrow the wrapped value. /// /// Returns `None` if the calling thread is not the one that wrapped the value. pubfn try_get_mut<'stack>(
&'stack mut self,
_proof: &'stack StackToken,
) -> Result<&'stack mut T, InvalidThreadAccess> { matchself.inner {
SemiStickyImpl::Fragile(refmut inner) => inner.try_get_mut(),
SemiStickyImpl::Sticky(refmut inner) => inner.try_get_mut(_proof),
}
}
}
#[test] #[should_panic] fn test_access_other_thread() { use std::thread; let val = SemiSticky::new(true);
thread::spawn(move || { crate::stack_token!(tok);
val.get(tok);
})
.join()
.unwrap();
}
#[test] fn test_drop_same_thread() { use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; let was_called = Arc::new(AtomicBool::new(false)); struct X(Arc<AtomicBool>); impl Drop for X { fn drop(&mutself) { self.0.store(true, Ordering::SeqCst);
}
} let val = SemiSticky::new(X(was_called.clone()));
mem::drop(val);
assert!(was_called.load(Ordering::SeqCst));
}
#[test] fn test_noop_drop_elsewhere() { use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread;
let was_called = Arc::new(AtomicBool::new(false));
{ let was_called = was_called.clone();
thread::spawn(move || { struct X(Arc<AtomicBool>); impl Drop for X { fn drop(&mutself) { self.0.store(true, Ordering::SeqCst);
}
}
let val = SemiSticky::new(X(was_called.clone()));
assert!(thread::spawn(move || { // moves it here but do not deallocate crate::stack_token!(tok);
val.try_get(tok).ok();
})
.join()
.is_ok());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.