use std::{
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
thread::{self, JoinHandle},
};
use crossbeam_channel::{self as channel, Sender};
use once_cell::sync::OnceCell;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[repr(transparent)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DeferDrop<T: Send + 'static> {
inner: ManuallyDrop<T>,
}
impl<T: Send + 'static> DeferDrop<T> {
#[inline]
pub fn new(value: T) -> Self {
DeferDrop {
inner: ManuallyDrop::new(value),
}
}
pub fn into_inner(mut this: Self) -> T {
let value = unsafe { ManuallyDrop::take(&mut this.inner) };
mem::forget(this);
value
}
}
static GARBAGE_CAN: OnceCell<GarbageCan> = OnceCell::new();
impl<T: Send + 'static> Drop for DeferDrop<T> {
fn drop(&mut self) {
GARBAGE_CAN
.get_or_init(|| GarbageCan::new("defer-drop background thread".to_owned()))
.throw_away(unsafe { ManuallyDrop::take(&mut self.inner) });
}
}
impl<T: Send + 'static> From<T> for DeferDrop<T> {
#[inline]
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Send + 'static> AsRef<T> for DeferDrop<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T: Send + 'static> AsMut<T> for DeferDrop<T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: Send + 'static> Deref for DeferDrop<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Send + 'static> DerefMut for DeferDrop<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[cfg(feature = "serde")]
impl<T: Serialize + Send + 'static> Serialize for DeferDrop<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_ref().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T: Deserialize<'de> + Send + 'static> Deserialize<'de> for DeferDrop<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(Self::new)
}
}
struct GarbageCan {
sender: Sender<Box<dyn Send>>,
handle: JoinHandle<()>,
}
impl GarbageCan {
fn new(name: String) -> Self {
let (sender, receiver) = channel::unbounded();
let handle = thread::Builder::new()
.name(name)
.spawn(move || receiver.into_iter().for_each(drop))
.expect("failed to spawn defer-drop background thread");
Self { sender, handle }
}
fn throw_away<T: Send + 'static>(&self, value: T) {
if thread::current().id() != self.handle.thread().id() {
let boxed = Box::new(value);
self.sender.send(boxed).unwrap();
}
}
}
#[cfg(test)]
mod tests {
use crossbeam_channel as channel;
use std::{
sync::{Arc, Mutex},
thread,
time::Duration,
};
use crate::DeferDrop;
#[test]
fn test() {
struct ThreadReporter {
chan: channel::Sender<thread::ThreadId>,
}
impl Drop for ThreadReporter {
fn drop(&mut self) {
self.chan.send(thread::current().id()).unwrap();
}
}
let (sender, receiver) = channel::bounded(1);
let this_thread_id = thread::current().id();
let thing = DeferDrop::new(ThreadReporter { chan: sender });
drop(thing);
match receiver.recv_timeout(Duration::from_secs(1)) {
Ok(id) => assert_ne!(
id, this_thread_id,
"thing wasn't dropped in a different thread"
),
Err(_) => panic!("thing wasn't dropped within one second of being dropped"),
}
}
#[test]
fn test_no_recursive_send() {
#[allow(dead_code)]
struct DropOrderRecorder<T> {
id: u32,
value: T,
record: Arc<Mutex<Vec<u32>>>,
}
impl<T> Drop for DropOrderRecorder<T> {
fn drop(&mut self) {
self.record.lock().unwrap().push(self.id)
}
}
let drop_order_record: Arc<Mutex<Vec<u32>>> = Default::default();
let value = DeferDrop::new(DropOrderRecorder {
id: 0,
record: drop_order_record.clone(),
value: [
DeferDrop::new(DropOrderRecorder {
id: 1,
record: drop_order_record.clone(),
value: [
DeferDrop::new(DropOrderRecorder {
id: 2,
record: drop_order_record.clone(),
value: (),
}),
DeferDrop::new(DropOrderRecorder {
id: 3,
record: drop_order_record.clone(),
value: (),
}),
],
}),
DeferDrop::new(DropOrderRecorder {
id: 4,
record: drop_order_record.clone(),
value: [
DeferDrop::new(DropOrderRecorder {
id: 5,
record: drop_order_record.clone(),
value: (),
}),
DeferDrop::new(DropOrderRecorder {
id: 6,
record: drop_order_record.clone(),
value: (),
}),
],
}),
],
});
drop(value);
loop {
thread::yield_now();
let lock = drop_order_record.lock().unwrap();
if lock.len() >= 7 {
break;
}
}
let lock = drop_order_record.lock().unwrap();
assert_eq!(lock.as_slice(), [0, 1, 2, 3, 4, 5, 6])
}
}