Skip to content

Commit 4fe09b4

Browse files
committed
Multithreaded InstanceStorage now handles poisoning
Fixes previously unrecoverable states due to temporary panics (that were caught using catch_unwind), and makes the behavior consistent with single-threaded RefCell.
1 parent f7cf30a commit 4fe09b4

File tree

1 file changed

+25
-3
lines changed

1 file changed

+25
-3
lines changed

godot-core/src/storage.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ mod multi_threaded {
202202

203203
pub fn is_bound(&self) -> bool {
204204
// Needs to borrow mutably, otherwise it succeeds if shared borrows are alive.
205-
self.user_instance.try_write().is_err()
205+
self.write_ignoring_poison().is_none()
206206
}
207207

208208
pub fn get(&self) -> sync::RwLockReadGuard<T> {
209-
self.user_instance.read().unwrap_or_else(|_e| {
209+
self.read_ignoring_poison().unwrap_or_else(|| {
210210
panic!(
211211
"Gd<T>::bind() failed, already bound; T = {}.\n \
212212
Make sure there is no &mut T live at the time.\n \
@@ -217,7 +217,7 @@ mod multi_threaded {
217217
}
218218

219219
pub fn get_mut(&self) -> sync::RwLockWriteGuard<T> {
220-
self.user_instance.write().unwrap_or_else(|_e| {
220+
self.write_ignoring_poison().unwrap_or_else(|| {
221221
panic!(
222222
"Gd<T>::bind_mut() failed, already bound; T = {}.\n \
223223
Make sure there is no &T or &mut T live at the time.\n \
@@ -227,6 +227,28 @@ mod multi_threaded {
227227
})
228228
}
229229

230+
/// Returns a write guard (even if poisoned), or `None` when the lock is held by another thread.
231+
/// This might need adjustment if threads should await locks.
232+
#[must_use]
233+
fn write_ignoring_poison(&self) -> Option<sync::RwLockWriteGuard<T>> {
234+
match self.user_instance.try_write() {
235+
Ok(guard) => Some(guard),
236+
Err(sync::TryLockError::Poisoned(poison_error)) => Some(poison_error.into_inner()),
237+
Err(sync::TryLockError::WouldBlock) => None,
238+
}
239+
}
240+
241+
/// Returns a read guard (even if poisoned), or `None` when the lock is held by another writing thread.
242+
/// This might need adjustment if threads should await locks.
243+
#[must_use]
244+
fn read_ignoring_poison(&self) -> Option<sync::RwLockReadGuard<T>> {
245+
match self.user_instance.try_read() {
246+
Ok(guard) => Some(guard),
247+
Err(sync::TryLockError::Poisoned(poison_error)) => Some(poison_error.into_inner()),
248+
Err(sync::TryLockError::WouldBlock) => None,
249+
}
250+
}
251+
230252
pub fn get_gd(&self) -> Gd<T>
231253
where
232254
T: Inherits<<T as GodotClass>::Base>,

0 commit comments

Comments
 (0)