rendy_resource/escape.rs
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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
//! This module provides wrapper for types that cannot be dropped silently.
//! Usually such types are required to be returned to their creator,
//! for example many Vulkan resources must be destroyed by the same
//! Vulkan instance that created them. Because they need some outside
//! context to be destroyed, Rust's `Drop` trait alone cannot handle them.
//! The `Escape` wrapper helps the user handle these values by sending the
//! underlying value to a `Terminal` when it is dropped. The user can
//! then remove those values from the `Terminal` elsewhere in the program
//! and destroy them however necessary.
//!
//! Users are encouraged to dispose of values manually while using `Escape`
//! as just a safety net.
use {
crossbeam_channel::{Receiver, Sender, TryRecvError},
std::{
iter::repeat,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr::{drop_in_place, read},
sync::Arc,
},
};
/// Allows values to "escape" dropping by sending them to the `Terminal`.
#[derive(Debug)]
pub struct Escape<T> {
value: ManuallyDrop<T>,
sender: Sender<T>,
}
impl<T> Escape<T> {
/// Escape value.
pub fn escape(value: T, terminal: &Terminal<T>) -> Self {
Escape {
value: ManuallyDrop::new(value),
sender: Sender::clone(&terminal.sender),
}
}
/// Unwrap escaping value.
/// This will effectivly prevent it from escaping.
pub fn unescape(escape: Self) -> T {
unsafe {
// Prevent `<Escape<T> as Drop>::drop` from being executed.
let mut escape = ManuallyDrop::new(escape);
// Release value from `ManuallyDrop`.
let value = read(&mut *escape.value);
// Drop sender. If it panics - value will be dropped.
// Relevant values are allowed to be dropped due to panic.
drop_in_place(&mut escape.sender);
value
}
}
/// Share escaped value.
pub fn share(escape: Self) -> Handle<T> {
escape.into()
}
}
impl<T> Deref for Escape<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.value
}
}
impl<T> DerefMut for Escape<T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.value
}
}
impl<T> Drop for Escape<T> {
fn drop(&mut self) {
unsafe {
// Read value from `ManuallyDrop` wrapper and send it over the channel.
match self.sender.send(read(&mut *self.value)) {
Ok(_) => {}
Err(_) => {
log::error!("`Escape` was dropped after a `Terminal`?");
}
}
}
}
}
/// This types allows the user to create `Escape` wrappers.
/// Receives values from dropped `Escape` instances that was created by this `Terminal`.
#[derive(Debug)]
pub struct Terminal<T> {
receiver: Receiver<T>,
sender: ManuallyDrop<Sender<T>>,
}
impl<T> Default for Terminal<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Terminal<T> {
/// Create new `Terminal`.
pub fn new() -> Self {
let (sender, receiver) = crossbeam_channel::unbounded();
Terminal {
sender: ManuallyDrop::new(sender),
receiver,
}
}
/// Wrap the value. It will be yielded by iterator returned by `Terminal::drain` if `Escape` will be dropped.
pub fn escape(&self, value: T) -> Escape<T> {
Escape::escape(value, &self)
}
// /// Check if `Escape` will send value to this `Terminal`.
// pub fn owns(&self, escape: &Escape<T>) -> bool {
// *self.sender == escape.sender
// }
/// Get iterator over values from dropped `Escape` instances that was created by this `Terminal`.
pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
repeat(()).scan(&mut self.receiver, move |receiver, ()| {
// trace!("Drain escape");
if !receiver.is_empty() {
receiver.recv().ok()
} else {
None
}
})
}
}
impl<T> Drop for Terminal<T> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.sender);
match self.receiver.try_recv() {
Err(TryRecvError::Disconnected) => {}
_ => {
log::error!("Terminal must be dropped after all `Escape`s");
}
}
}
}
}
/// Allows values to "escape" dropping by sending them to the `Terminal`.
/// Permit sharing unlike [`Escape`]
///
/// [`Escape`]: ./struct.Escape.html
#[derive(Debug)]
pub struct Handle<T> {
inner: Arc<Escape<T>>,
}
impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
Handle {
inner: self.inner.clone(),
}
}
}
impl<T> From<Escape<T>> for Handle<T> {
fn from(value: Escape<T>) -> Self {
Handle {
inner: Arc::new(value),
}
}
}
impl<T> Deref for Handle<T> {
type Target = T;
fn deref(&self) -> &T {
&**self.inner
}
}