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