#![no_std]
#![cfg_attr(feature = "doc", feature(doc_auto_cfg))]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
pub mod thread;
#[cfg(feature = "std")]
#[doc(inline)]
pub use thread::{ThreadBackdrop, TrashThreadBackdrop, ThreadStrategy, TrashThreadStrategy};
#[cfg(feature = "tokio")]
pub mod tokio;
#[cfg(feature = "tokio")]
#[doc(inline)]
pub use crate::tokio::{TokioTaskBackdrop, TokioBlockingTaskBackdrop, TokioTaskStrategy, TokioBlockingTaskStrategy};
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::mem::ManuallyDrop;
pub trait BackdropStrategy<T> {
fn execute(droppable: T);
}
#[repr(transparent)]
pub struct Backdrop<T, S: BackdropStrategy<T>> {
val: ManuallyDrop<T>,
_marker: PhantomData<S>,
}
impl<T, Strategy: BackdropStrategy<T>> Backdrop<T, Strategy> {
#[inline]
pub fn new(val: T) -> Self {
Self {
val: ManuallyDrop::new(val),
_marker: PhantomData,
}
}
#[inline]
pub fn into_inner(mut this: Self) -> T {
unsafe {
let inner = ManuallyDrop::take(&mut this.val);
core::mem::forget(this);
inner
}
}
pub fn change_strategy<S2: BackdropStrategy<T>>(this: Self) -> Backdrop<T, S2> {
Backdrop::<T, S2>::new(Backdrop::into_inner(this))
}
}
impl<T, Strategy: BackdropStrategy<T>> Drop for Backdrop<T, Strategy> {
#[inline]
fn drop(&mut self) {
let inner = unsafe { ManuallyDrop::take(&mut self.val)};
Strategy::execute(inner)
}
}
impl<T, S: BackdropStrategy<T>> core::ops::Deref for Backdrop<T, S> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.val.deref()
}
}
impl<T, S: BackdropStrategy<T>> DerefMut for Backdrop<T, S> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.val.deref_mut()
}
}
impl<T: core::fmt::Debug, S> core::fmt::Debug for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(&**self, f)
}
}
impl<T: core::fmt::Display, S> core::fmt::Display for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&**self, f)
}
}
impl<T: Clone, S> Clone for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn clone(&self) -> Self {
Self::new(self.deref().clone())
}
}
impl<T: core::cmp::PartialEq, S> core::cmp::PartialEq for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn eq(&self, other: &Self) -> bool {
self.deref().eq(other.deref())
}
}
impl<T: core::cmp::Eq, S> core::cmp::Eq for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{ }
impl<T: core::cmp::PartialOrd, S> core::cmp::PartialOrd for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.deref().partial_cmp(other.deref())
}
}
impl<T: core::cmp::Ord, S> core::cmp::Ord for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.deref().cmp(other.deref())
}
}
impl<T: core::hash::Hash, S> core::hash::Hash for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state)
}
}
impl<T, S> From<T> for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
fn from(val: T) -> Self {
Backdrop::new(val)
}
}
pub struct TrivialStrategy();
impl<T> BackdropStrategy<T> for TrivialStrategy {
#[inline]
fn execute(droppable: T) {
core::mem::drop(droppable)
}
}
pub type TrivialBackdrop<T> = Backdrop<T, TrivialStrategy>;
pub struct LeakStrategy();
impl<T> BackdropStrategy<T> for LeakStrategy {
#[inline]
fn execute(droppable: T) {
core::mem::forget(droppable)
}
}
pub type LeakBackdrop<T> = Backdrop<T, LeakStrategy>;
#[cfg(feature = "std")]
pub struct DebugStrategy<InnerStrategy>(PhantomData<InnerStrategy>);
#[cfg(feature = "std")]
impl<T, InnerStrategy> BackdropStrategy<T> for DebugStrategy<InnerStrategy>
where
T: std::fmt::Debug,
InnerStrategy: BackdropStrategy<T>,
{
#[inline]
fn execute(droppable: T) {
use std::println;
println!("Using BackdropStrategy '{}' to drop value {:?}", std::any::type_name::<InnerStrategy>(), &droppable);
InnerStrategy::execute(droppable)
}
}
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, collections::VecDeque};
#[cfg(feature = "alloc")]
use core::cell::RefCell;
#[cfg(feature = "alloc")]
use core::any::Any;
#[cfg(feature = "std")]
std::thread_local!{
static TRASH_QUEUE: RefCell<VecDeque<Box<dyn Any>>> = VecDeque::new().into();
}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
static mut TRASH_QUEUE: Option<core::cell::RefCell<VecDeque<Box<dyn core::any::Any>>>> = None;
#[cfg(feature = "std")]
fn with_single_threaded_trash_queue(closure: impl FnOnce(&RefCell<VecDeque<Box<dyn Any>>>)) {
TRASH_QUEUE.with(|tq_cell| {
closure(tq_cell);
});
}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
fn with_single_threaded_trash_queue(closure: impl FnOnce(&RefCell<VecDeque<Box<dyn Any>>>)) {
let tq_ref = unsafe { &mut TRASH_QUEUE };
if tq_ref.is_none() {
*tq_ref = Some(VecDeque::new().into());
}
closure(unsafe { &TRASH_QUEUE.as_ref().unwrap() });
}
#[cfg(feature = "alloc")]
pub struct TrashQueueStrategy();
#[cfg(feature = "alloc")]
impl TrashQueueStrategy {
pub fn ensure_initialized() {
with_single_threaded_trash_queue(|_tq_cell| {});
}
pub fn cleanup_one() -> bool {
let mut queue_nonempty = false;
with_single_threaded_trash_queue(|tq_cell| {
let mut tq = tq_cell.borrow_mut();
let item = tq.pop_front();
core::mem::drop(item);
queue_nonempty = tq.is_empty();
});
queue_nonempty
}
pub fn cleanup_all() {
with_single_threaded_trash_queue(|tq_cell| {
let mut tq = tq_cell.borrow_mut();
while let Some(item) = tq.pop_front() {
core::mem::drop(item);
}
});
}
pub fn cleanup_on_exit<R>(closure: impl FnOnce() -> R) -> R {
TrashQueueStrategy::ensure_initialized();
let outcome = closure();
TrashQueueStrategy::cleanup_all();
outcome
}
}
#[cfg(feature = "alloc")]
impl<T: 'static> BackdropStrategy<T> for TrashQueueStrategy {
fn execute(droppable: T) {
let boxed: Box<dyn core::any::Any> = Box::new(droppable);
with_single_threaded_trash_queue(|tq_cell| {
let mut tq = tq_cell.borrow_mut();
tq.push_back(boxed);
});
}
}
#[cfg(feature = "rkyv")]
impl<T: rkyv::Archive, S> rkyv::Archive for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
type Archived = rkyv::Archived<T>;
type Resolver = rkyv::Resolver<T>;
unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
self.deref().resolve(pos, resolver, out);
}
}
#[cfg(feature = "rkyv")]
impl<Ser, T: rkyv::Archive + rkyv::Serialize<Ser>, S> rkyv::Serialize<Ser> for Backdrop<T, S>
where
Ser: rkyv::Fallible,
S: BackdropStrategy<T>,
{
fn serialize(&self, serializer: &mut Ser) -> Result<Self::Resolver, <Ser as rkyv::Fallible>::Error> {
self.deref().serialize(serializer)
}
}
#[cfg(feature = "rkyv")]
impl<Des, T, S> rkyv::Deserialize<Backdrop<T, S>, Des> for rkyv::Archived<T>
where
T: rkyv::Archive,
rkyv::Archived<T>: rkyv::Deserialize<T, Des>,
Des: rkyv::Fallible,
S: BackdropStrategy<T>,
{
fn deserialize(&self, deserializer: &mut Des) -> Result<Backdrop<T, S>, <Des as rkyv::Fallible>::Error> {
let inner: T = self.deserialize(deserializer)?;
Ok(Backdrop::new(inner))
}
}
#[cfg(feature = "bytecheck")]
impl<C: ?Sized, T: bytecheck::CheckBytes<C>, S> bytecheck::CheckBytes<C> for Backdrop<T, S>
where
S: BackdropStrategy<T>,
{
type Error = <T as bytecheck::CheckBytes<C>>::Error;
unsafe fn check_bytes<'a>(value: *const Self, context: &mut C) -> Result<&'a Self, Self::Error>{
let inner_ref = bytecheck::CheckBytes::check_bytes(value as *const T, context)?;
Ok(core::mem::transmute(inner_ref))
}
}