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}