Skip to content

Commit be74d96

Browse files
committed
Global: changed to wrap Mutex<OnceLock>
1 parent 4dc2720 commit be74d96

File tree

1 file changed

+44
-107
lines changed

1 file changed

+44
-107
lines changed

godot-ffi/src/global.rs

Lines changed: 44 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use std::sync::{Mutex, MutexGuard, TryLockError};
8+
use std::cell::OnceCell;
9+
use std::sync::{Mutex, MutexGuard, PoisonError, TryLockError};
910

1011
/// Ergonomic global variables.
1112
///
@@ -25,7 +26,8 @@ use std::sync::{Mutex, MutexGuard, TryLockError};
2526
/// For access, you should primarily use [`lock()`](Self::lock). There is also [`try_lock()`](Self::try_lock) for special cases.
2627
pub struct Global<T> {
2728
// When needed, this could be changed to use RwLock and separate read/write guards.
28-
value: Mutex<InitState<T>>,
29+
value: Mutex<OnceCell<T>>,
30+
init_fn: fn() -> T,
2931
}
3032

3133
impl<T> Global<T> {
@@ -35,7 +37,8 @@ impl<T> Global<T> {
3537
pub const fn new(init_fn: fn() -> T) -> Self {
3638
// Note: could be generalized to F: FnOnce() -> T + Send. See also once_cell::Lazy<T, F>.
3739
Self {
38-
value: Mutex::new(InitState::Pending(init_fn)),
40+
value: Mutex::new(OnceCell::new()),
41+
init_fn,
3942
}
4043
}
4144

@@ -57,72 +60,39 @@ impl<T> Global<T> {
5760
/// If the initialization function panics. Once that happens, the global is considered poisoned and all future calls to `lock()` will panic.
5861
/// This can currently not be recovered from.
5962
pub fn lock(&self) -> GlobalGuard<'_, T> {
60-
let mutex_guard = self
61-
.value
62-
.lock()
63-
.expect("Global<T> poisoned; a thread has panicked while holding a lock to it");
63+
let guard = self.value.lock().unwrap_or_else(PoisonError::into_inner);
64+
guard.get_or_init(self.init_fn);
6465

65-
let guard = Self::ensure_init(mutex_guard, true)
66-
.unwrap_or_else(|| panic!("previous Global<T> initialization failed due to panic"));
67-
68-
guard
66+
// SAFETY: above line guarantees init
67+
unsafe { GlobalGuard::new_unchecked(guard) }
6968
}
7069

71-
/// Non-panicking access with error introspection.
70+
/// Non-blocking access with error introspection.
7271
pub fn try_lock(&self) -> Result<GlobalGuard<'_, T>, GlobalLockError<'_, T>> {
73-
let guard = match self.value.try_lock() {
74-
Ok(mutex_guard) => Self::ensure_init(mutex_guard, false),
75-
Err(TryLockError::WouldBlock) => {
76-
return Err(GlobalLockError::WouldBlock);
77-
}
78-
Err(TryLockError::Poisoned(poisoned)) => {
79-
return Err(GlobalLockError::Poisoned {
80-
// We can likely use `new_unchecked` here, but verifying that it's safe would need somewhat tricky reasoning.
81-
// Since this error condition isn't very common, it is likely not very important to optimize access to the value here.
82-
// Especially since most users will likely not want to access it anyway.
83-
circumvent: GlobalGuard::new(poisoned.into_inner())
84-
.expect("Poisoned global guard should always be initialized"),
85-
});
86-
}
87-
};
72+
/// Initializes the cell and returns a guard.
73+
fn init<'mutex: 'cell, 'cell, T>(
74+
g: MutexGuard<'mutex, OnceCell<T>>,
75+
init_fn: fn() -> T,
76+
) -> Result<GlobalGuard<'cell, T>, GlobalLockError<'cell, T>> {
77+
// initialize the cell
78+
std::panic::catch_unwind(|| g.get_or_init(init_fn))
79+
.map_err(|_| GlobalLockError::InitFailed)?;
80+
81+
// SAFETY: `get_or_init()` cant panic, therefore the object is guaranteed to be initialized.
82+
Ok(unsafe { GlobalGuard::new_unchecked(g) })
83+
}
8884

89-
guard.ok_or(GlobalLockError::InitFailed)
90-
}
85+
match self.value.try_lock() {
86+
Ok(guard) => init(guard, self.init_fn),
87+
Err(TryLockError::WouldBlock) => Err(GlobalLockError::WouldBlock),
9188

92-
fn ensure_init(
93-
mut mutex_guard: MutexGuard<'_, InitState<T>>,
94-
may_panic: bool,
95-
) -> Option<GlobalGuard<'_, T>> {
96-
let init_fn = match &mut *mutex_guard {
97-
InitState::Initialized(_) => {
98-
// SAFETY: `mutex_guard` is `Initialized`.
99-
return Some(unsafe { GlobalGuard::new_unchecked(mutex_guard) });
89+
// this is a cold branch, where the initialization function panicked.
90+
Err(TryLockError::Poisoned(x)) => {
91+
// we do the same things as in the hot branch
92+
let circumvent = init(x.into_inner(), self.init_fn)?;
93+
Err(GlobalLockError::Poisoned { circumvent })
10094
}
101-
InitState::Failed => {
102-
return None;
103-
}
104-
InitState::Pending(init_fn) => init_fn,
105-
};
106-
107-
// Unwinding should be safe here, as there is no unsafe code relying on it.
108-
let init_fn = std::panic::AssertUnwindSafe(init_fn);
109-
match std::panic::catch_unwind(init_fn) {
110-
Ok(value) => *mutex_guard = InitState::Initialized(value),
111-
Err(e) => {
112-
eprintln!("panic during Global<T> initialization");
113-
*mutex_guard = InitState::Failed;
114-
115-
if may_panic {
116-
std::panic::resume_unwind(e);
117-
} else {
118-
// Note: this currently swallows panic.
119-
return None;
120-
}
121-
}
122-
};
123-
124-
// SAFETY: `mutex_guard` was either set to `Initialized` above, or we returned from the function.
125-
Some(unsafe { GlobalGuard::new_unchecked(mutex_guard) })
95+
}
12696
}
12797
}
12898

@@ -131,57 +101,49 @@ impl<T> Global<T> {
131101

132102
// Encapsulate private fields.
133103
mod global_guard {
104+
use super::*;
134105
use std::ops::{Deref, DerefMut};
135-
use std::sync::MutexGuard;
136-
137-
use super::InitState;
138106

139107
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
140108
pub struct GlobalGuard<'a, T> {
141-
// Safety invariant: Is `Initialized`.
142-
mutex_guard: MutexGuard<'a, InitState<T>>,
109+
mutex_guard: MutexGuard<'a, OnceCell<T>>,
143110
}
144111

145112
impl<'a, T> GlobalGuard<'a, T> {
146-
pub(super) fn new(mutex_guard: MutexGuard<'a, InitState<T>>) -> Option<Self> {
147-
match &*mutex_guard {
148-
InitState::Initialized(_) => Some(Self { mutex_guard }),
113+
pub(super) fn new(mutex_guard: MutexGuard<'a, OnceCell<T>>) -> Option<Self> {
114+
match mutex_guard.get() {
115+
Some(_) => Some(Self { mutex_guard }),
149116
_ => None,
150117
}
151118
}
152119

153120
/// # Safety
154121
///
155-
/// The value must be `Initialized`.
156-
pub(super) unsafe fn new_unchecked(mutex_guard: MutexGuard<'a, InitState<T>>) -> Self {
157-
debug_assert!(matches!(*mutex_guard, InitState::Initialized(_)));
158-
122+
/// The value must be initialized.
123+
pub(super) unsafe fn new_unchecked(mutex_guard: MutexGuard<'a, OnceCell<T>>) -> Self {
124+
debug_assert!(mutex_guard.get().is_some(), "safety precondition violated");
159125
Self::new(mutex_guard).unwrap_unchecked()
160126
}
161127
}
162128

163129
impl<T> Deref for GlobalGuard<'_, T> {
164130
type Target = T;
165-
166131
fn deref(&self) -> &Self::Target {
167-
// SAFETY: `self` is `Initialized`.
168-
unsafe { self.mutex_guard.as_initialized().unwrap_unchecked() }
132+
// SAFETY: [`GlobalGuard::new`] being the sole constructor guarantees that the cell is initialized.
133+
unsafe { self.mutex_guard.get().unwrap_unchecked() }
169134
}
170135
}
171136

172137
impl<T> DerefMut for GlobalGuard<'_, T> {
173138
fn deref_mut(&mut self) -> &mut Self::Target {
174-
// SAFETY: `self` is `Initialized`.
175-
unsafe { self.mutex_guard.as_initialized_mut().unwrap_unchecked() }
139+
// SAFETY: [`GlobalGuard::new`] being the sole constructor guarantees that the cell is initialized.
140+
unsafe { self.mutex_guard.get_mut().unwrap_unchecked() }
176141
}
177142
}
178143
}
179144

180145
pub use global_guard::GlobalGuard;
181146

182-
// ----------------------------------------------------------------------------------------------------------------------------------------------
183-
// Errors
184-
185147
/// Guard that temporarily gives access to a `Global<T>`'s inner value.
186148
pub enum GlobalLockError<'a, T> {
187149
/// The mutex is currently locked by another thread.
@@ -194,31 +156,6 @@ pub enum GlobalLockError<'a, T> {
194156
InitFailed,
195157
}
196158

197-
// ----------------------------------------------------------------------------------------------------------------------------------------------
198-
// Internals
199-
200-
enum InitState<T> {
201-
Initialized(T),
202-
Pending(fn() -> T),
203-
Failed,
204-
}
205-
206-
impl<T> InitState<T> {
207-
fn as_initialized(&self) -> Option<&T> {
208-
match self {
209-
InitState::Initialized(t) => Some(t),
210-
_ => None,
211-
}
212-
}
213-
214-
fn as_initialized_mut(&mut self) -> Option<&mut T> {
215-
match self {
216-
InitState::Initialized(t) => Some(t),
217-
_ => None,
218-
}
219-
}
220-
}
221-
222159
// ----------------------------------------------------------------------------------------------------------------------------------------------
223160
// Tests
224161

0 commit comments

Comments
 (0)