1use setjmp::{jmp_buf, longjmp, setjmp};
5use std::{
6 any::Any,
7 cell::{Ref, RefCell, RefMut},
8 panic::PanicInfo,
9};
10
11thread_local! {
12 static NEXT_ROOM_ID: std::cell::RefCell<u64> = std::cell::RefCell::new(0);
13 static ROOMS: std::cell::RefCell<Vec<Room>> = std::cell::RefCell::new(Vec::new());
14}
15
16pub struct Room {
17 id: u64,
18 data: Vec<RefCell<Option<Box<dyn Any>>>>,
19 jmp_buf: jmp_buf,
20 #[allow(dead_code)]
21 old_panic_hook: Box<dyn Fn(&std::panic::PanicInfo<'_>) + Sync + Send>,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
25pub struct Handle<T> {
26 id: u64,
27 index: usize,
28 _phantom: std::marker::PhantomData<T>,
29}
30
31impl Room {
33 fn push_room() -> Option<()> {
34 let id = NEXT_ROOM_ID.with(|next_room_id| {
35 let mut next_room_id = next_room_id.borrow_mut();
36 let id = *next_room_id;
37 *next_room_id += 1;
38 id
39 });
40 ROOMS.with(|rooms| {
41 let mut rooms = rooms.try_borrow_mut().ok()?;
42 rooms.reserve(1);
43 let new_panic_hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> =
44 Box::new(|_panic_info| {
45 let jmp_buf = Self::current_jmp_buf();
46 if let Some(jmp_buf) = jmp_buf {
47 unsafe { longjmp(jmp_buf, 1) };
48 }
49 });
50 let data = Vec::new();
51 let jmp_buf = unsafe { std::mem::zeroed() };
52 let old_panic_hook = std::panic::take_hook();
53 let room = Room {
54 id,
55 data,
56 jmp_buf,
57 old_panic_hook,
58 };
59 std::panic::set_hook(new_panic_hook);
60 rooms.push(room);
61 Some(())
62 })
63 }
64
65 fn pop_room() -> Option<()> {
66 ROOMS.with(|rooms| {
67 let mut rooms = rooms.try_borrow_mut().ok()?;
68 if let Some(_room) = rooms.pop() {
69 Some(())
75 } else {
76 None
77 }
78 })
79 }
80
81 fn current_jmp_buf() -> Option<*mut jmp_buf> {
82 Room::with_current_mut(|room| Some(&mut room?.jmp_buf as *mut jmp_buf))
83 }
84}
85
86impl Room {
88 pub fn with_current_mut<T, F>(f: F) -> Option<T>
89 where
90 F: FnOnce(Option<&mut Room>) -> Option<T>,
91 {
92 ROOMS.with(|rooms| {
93 let mut rooms = rooms.try_borrow_mut().ok()?;
94 let room = rooms.last_mut();
95 f(room)
96 })
97 }
98
99 pub fn with_current<T, F>(f: F) -> Option<T>
100 where
101 F: FnOnce(Option<&Room>) -> Option<T>,
102 {
103 ROOMS.with(|rooms| {
104 let rooms = rooms.try_borrow().ok()?;
105 let room = rooms.last();
106 f(room)
107 })
108 }
109
110 pub fn contain_panics<T, F>(f: F) -> Option<T>
111 where
112 F: FnOnce() -> T,
113 {
114 Room::push_room()?;
115 let Some(jmp_buf) = Room::current_jmp_buf() else {
116 Room::pop_room();
117 return None;
118 };
119 let res = if unsafe { setjmp(jmp_buf) } == 0 {
120 Some(f())
121 } else {
122 None
126 };
127 Room::pop_room();
128 res
129 }
130
131 pub fn alloc<T: 'static>(&mut self, value: T) -> Handle<T> {
132 let index = self.data.len();
133 self.data.push(RefCell::new(Some(Box::new(value))));
134 Handle {
135 id: self.id,
136 index,
137 _phantom: std::marker::PhantomData,
138 }
139 }
140
141 pub fn get<T: 'static>(&self, handle: Handle<T>) -> Option<Ref<T>> {
142 if handle.id == self.id {
143 if let Some(r) = self.data.get(handle.index)?.try_borrow().ok() {
144 Ref::filter_map(r, |any_opt| {
145 if let Some(any) = any_opt {
146 any.downcast_ref::<T>()
147 } else {
148 None
149 }
150 })
151 .ok()
152 } else {
153 None
154 }
155 } else {
156 None
157 }
158 }
159
160 pub fn get_mut<T: 'static>(&self, handle: Handle<T>) -> Option<RefMut<T>> {
161 if handle.id == self.id {
162 if let Some(r) = self.data.get(handle.index)?.try_borrow_mut().ok() {
163 RefMut::filter_map(r, |any_opt| {
164 if let Some(any) = any_opt {
165 any.downcast_mut::<T>()
166 } else {
167 None
168 }
169 })
170 .ok()
171 } else {
172 None
173 }
174 } else {
175 None
176 }
177 }
178
179 pub fn take<T: 'static>(&mut self, handle: Handle<T>) -> Option<T> {
180 if handle.id == self.id {
181 self.data
182 .get(handle.index)?
183 .borrow_mut()
184 .take()?
185 .downcast()
186 .ok()
187 .map(|x| *x)
188 } else {
189 None
190 }
191 }
192}