rendy_resource/
escape.rs

1//! This module provides wrapper for types that cannot be dropped silently.
2//! Usually such types are required to be returned to their creator,
3//! for example many Vulkan resources must be destroyed by the same
4//! Vulkan instance that created them.  Because they need some outside
5//! context to be destroyed, Rust's `Drop` trait alone cannot handle them.
6//! The `Escape` wrapper helps the user handle these values by sending the
7//! underlying value to a `Terminal` when it is dropped.  The user can
8//! then remove those values from the `Terminal` elsewhere in the program
9//! and destroy them however necessary.
10//!
11//! Users are encouraged to dispose of values manually while using `Escape`
12//! as just a safety net.
13
14use {
15    crossbeam_channel::{Receiver, Sender, TryRecvError},
16    std::{
17        iter::repeat,
18        mem::ManuallyDrop,
19        ops::{Deref, DerefMut},
20        ptr::{drop_in_place, read},
21        sync::Arc,
22    },
23};
24
25/// Allows values to "escape" dropping by sending them to the `Terminal`.
26#[derive(Debug)]
27pub struct Escape<T> {
28    value: ManuallyDrop<T>,
29    sender: Sender<T>,
30}
31
32impl<T> Escape<T> {
33    /// Escape value.
34    pub fn escape(value: T, terminal: &Terminal<T>) -> Self {
35        Escape {
36            value: ManuallyDrop::new(value),
37            sender: Sender::clone(&terminal.sender),
38        }
39    }
40
41    /// Unwrap escaping value.
42    /// This will effectivly prevent it from escaping.
43    pub fn unescape(escape: Self) -> T {
44        unsafe {
45            // Prevent `<Escape<T> as Drop>::drop` from being executed.
46            let mut escape = ManuallyDrop::new(escape);
47
48            // Release value from `ManuallyDrop`.
49            let value = read(&mut *escape.value);
50
51            // Drop sender. If it panics - value will be dropped.
52            // Relevant values are allowed to be dropped due to panic.
53            drop_in_place(&mut escape.sender);
54            value
55        }
56    }
57
58    /// Share escaped value.
59    pub fn share(escape: Self) -> Handle<T> {
60        escape.into()
61    }
62}
63
64impl<T> Deref for Escape<T> {
65    type Target = T;
66    fn deref(&self) -> &T {
67        &*self.value
68    }
69}
70
71impl<T> DerefMut for Escape<T> {
72    fn deref_mut(&mut self) -> &mut T {
73        &mut *self.value
74    }
75}
76
77impl<T> Drop for Escape<T> {
78    fn drop(&mut self) {
79        unsafe {
80            // Read value from `ManuallyDrop` wrapper and send it over the channel.
81            match self.sender.send(read(&mut *self.value)) {
82                Ok(_) => {}
83                Err(_) => {
84                    log::error!("`Escape` was dropped after a `Terminal`?");
85                }
86            }
87        }
88    }
89}
90
91/// This types allows the user to create `Escape` wrappers.
92/// Receives values from dropped `Escape` instances that was created by this `Terminal`.
93#[derive(Debug)]
94pub struct Terminal<T> {
95    receiver: Receiver<T>,
96    sender: ManuallyDrop<Sender<T>>,
97}
98
99impl<T> Default for Terminal<T> {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl<T> Terminal<T> {
106    /// Create new `Terminal`.
107    pub fn new() -> Self {
108        let (sender, receiver) = crossbeam_channel::unbounded();
109        Terminal {
110            sender: ManuallyDrop::new(sender),
111            receiver,
112        }
113    }
114
115    /// Wrap the value. It will be yielded by iterator returned by `Terminal::drain` if `Escape` will be dropped.
116    pub fn escape(&self, value: T) -> Escape<T> {
117        Escape::escape(value, &self)
118    }
119
120    // /// Check if `Escape` will send value to this `Terminal`.
121    // pub fn owns(&self, escape: &Escape<T>) -> bool {
122    //     *self.sender == escape.sender
123    // }
124
125    /// Get iterator over values from dropped `Escape` instances that was created by this `Terminal`.
126    pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
127        repeat(()).scan(&mut self.receiver, move |receiver, ()| {
128            // trace!("Drain escape");
129            if !receiver.is_empty() {
130                receiver.recv().ok()
131            } else {
132                None
133            }
134        })
135    }
136}
137
138impl<T> Drop for Terminal<T> {
139    fn drop(&mut self) {
140        unsafe {
141            ManuallyDrop::drop(&mut self.sender);
142            match self.receiver.try_recv() {
143                Err(TryRecvError::Disconnected) => {}
144                _ => {
145                    log::error!("Terminal must be dropped after all `Escape`s");
146                }
147            }
148        }
149    }
150}
151
152/// Allows values to "escape" dropping by sending them to the `Terminal`.
153/// Permit sharing unlike [`Escape`]
154///
155/// [`Escape`]: ./struct.Escape.html
156#[derive(Debug)]
157pub struct Handle<T> {
158    inner: Arc<Escape<T>>,
159}
160
161impl<T> Clone for Handle<T> {
162    fn clone(&self) -> Self {
163        Handle {
164            inner: self.inner.clone(),
165        }
166    }
167}
168
169impl<T> From<Escape<T>> for Handle<T> {
170    fn from(value: Escape<T>) -> Self {
171        Handle {
172            inner: Arc::new(value),
173        }
174    }
175}
176
177impl<T> Deref for Handle<T> {
178    type Target = T;
179
180    fn deref(&self) -> &T {
181        &**self.inner
182    }
183}