bones_scripting/lua/
freeze.rs1use std::{cell::RefCell, marker::PhantomData, mem, rc::Rc};
4
5#[derive(Debug, Copy, Clone, Eq, PartialEq)]
6pub enum AccessError {
7 Expired,
8 BadBorrow,
9}
10
11pub struct Frozen<F: for<'f> Freeze<'f>> {
20 inner: Rc<RefCell<Option<<F as Freeze<'static>>::Frozen>>>,
21}
22
23pub trait Freeze<'f>: 'static {
24 type Frozen: 'f;
25}
26
27pub struct DynFreeze<T: ?Sized>(PhantomData<T>);
28
29impl<'f, T: ?Sized + for<'a> Freeze<'a>> Freeze<'f> for DynFreeze<T> {
30 type Frozen = <T as Freeze<'f>>::Frozen;
31}
32
33#[macro_export]
34macro_rules! Freeze {
35 ($f:lifetime => $frozen:ty) => {
36 $crate::lua::freeze::DynFreeze::<
37 dyn for<$f> $crate::lua::freeze::Freeze<$f, Frozen = $frozen>,
38 >
39 };
40 ($frozen:ty) => {
41 $crate::lua::freeze::Freeze!['freeze => $frozen]
42 };
43}
44
45pub use crate::Freeze;
46
47impl<F: for<'a> Freeze<'a>> Clone for Frozen<F> {
48 fn clone(&self) -> Self {
49 Self {
50 inner: self.inner.clone(),
51 }
52 }
53}
54
55impl<F: for<'a> Freeze<'a>> Default for Frozen<F> {
56 fn default() -> Self {
57 Self {
58 inner: Rc::new(RefCell::new(None)),
59 }
60 }
61}
62
63impl<F: for<'a> Freeze<'a>> Frozen<F> {
64 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn in_scope<R>(value: <F as Freeze>::Frozen, cb: impl FnOnce(Self) -> R) -> R {
70 let f = Self::new();
71 let p = f.clone();
72 FrozenScope::new().freeze(&f, value).scope(move || cb(p))
73 }
74
75 pub fn is_valid(&self) -> bool {
77 if let Ok(b) = self.inner.try_borrow() {
78 b.is_some()
79 } else {
80 true
81 }
82 }
83
84 pub fn try_with<R>(
85 &self,
86 f: impl for<'f> FnOnce(&<F as Freeze<'f>>::Frozen) -> R,
87 ) -> Result<R, AccessError> {
88 Ok(f(self
89 .inner
90 .try_borrow()
91 .map_err(|_| AccessError::BadBorrow)?
92 .as_ref()
93 .ok_or(AccessError::Expired)?))
94 }
95
96 pub fn with<R>(&self, f: impl for<'f> FnOnce(&<F as Freeze<'f>>::Frozen) -> R) -> R {
100 self.try_with(f).unwrap()
101 }
102
103 pub fn try_with_mut<R>(
104 &self,
105 f: impl for<'f> FnOnce(&mut <F as Freeze<'f>>::Frozen) -> R,
106 ) -> Result<R, AccessError> {
107 Ok(f(self
108 .inner
109 .try_borrow_mut()
110 .map_err(|_| AccessError::BadBorrow)?
111 .as_mut()
112 .ok_or(AccessError::Expired)?))
113 }
114
115 pub fn with_mut<R>(&self, f: impl for<'f> FnOnce(&mut <F as Freeze<'f>>::Frozen) -> R) -> R {
118 self.try_with_mut(f).unwrap()
119 }
120}
121
122pub struct FrozenScope<D = ()>(D);
125
126impl Default for FrozenScope<()> {
127 fn default() -> Self {
128 FrozenScope(())
129 }
130}
131
132impl FrozenScope<()> {
133 pub fn new() -> Self {
134 Self(())
135 }
136}
137
138impl<D: DropGuard> FrozenScope<D> {
139 pub fn freeze<'h, 'f, F: for<'a> Freeze<'a>>(
141 self,
142 handle: &'h Frozen<F>,
143 value: <F as Freeze<'f>>::Frozen,
144 ) -> FrozenScope<(FreezeGuard<'h, 'f, F>, D)> {
145 FrozenScope((
146 FreezeGuard {
147 value: Some(value),
148 handle,
149 },
150 self.0,
151 ))
152 }
153
154 pub fn scope<R>(mut self, cb: impl FnOnce() -> R) -> R {
163 unsafe {
177 self.0.set();
178 }
179 let r = cb();
180 drop(self.0);
181 r
182 }
183}
184
185pub trait DropGuard {
186 unsafe fn set(&mut self);
196}
197
198impl DropGuard for () {
199 unsafe fn set(&mut self) {}
200}
201
202impl<A: DropGuard, B: DropGuard> DropGuard for (A, B) {
203 unsafe fn set(&mut self) {
204 self.0.set();
205 self.1.set();
206 }
207}
208
209pub struct FreezeGuard<'h, 'f, F: for<'a> Freeze<'a>> {
210 value: Option<<F as Freeze<'f>>::Frozen>,
211 handle: &'h Frozen<F>,
212}
213
214impl<'h, 'f, F: for<'a> Freeze<'a>> Drop for FreezeGuard<'h, 'f, F> {
215 fn drop(&mut self) {
216 if let Ok(mut v) = self.handle.inner.try_borrow_mut() {
217 *v = None;
218 } else {
219 eprintln!("impossible! freeze lock held during drop guard, aborting!");
226 std::process::abort()
227 }
228 }
229}
230
231impl<'h, 'f, F: for<'a> Freeze<'a>> DropGuard for FreezeGuard<'h, 'f, F> {
232 unsafe fn set(&mut self) {
233 assert!(
234 !self.handle.is_valid(),
235 "handle already used in another `FrozenScope::scope` call"
236 );
237 *self.handle.inner.borrow_mut() = Some(mem::transmute::<
238 <F as Freeze<'f>>::Frozen,
239 <F as Freeze<'static>>::Frozen,
240 >(self.value.take().unwrap()));
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn test_freeze_works() {
250 struct F<'a>(&'a i32);
251
252 let i = 4;
253 Frozen::<Freeze![F<'freeze>]>::in_scope(F(&i), |f| {
254 f.with(|f| {
255 assert_eq!(*f.0, 4);
256 });
257 });
258 }
259
260 #[test]
261 fn test_freeze_expires() {
262 struct F<'a>(&'a i32);
263
264 type FrozenF = Frozen<Freeze![F<'freeze>]>;
265
266 let mut outer: Option<FrozenF> = None;
267
268 let i = 4;
269 FrozenF::in_scope(F(&i), |f| {
270 outer = Some(f.clone());
271 });
272
273 assert_eq!(
274 outer.unwrap().try_with(|f| {
275 assert_eq!(*f.0, 4);
276 }),
277 Err(AccessError::Expired)
278 );
279 }
280}