Replace bespoke LazyLock/OnceLock with spin re-exports

Presumably notably slower on platforms with std, yet only when compiled with old
versions of Rust for which the option is this or no support anyways.
This commit is contained in:
Luke Parker
2025-08-19 18:10:33 -04:00
parent 432daae1d1
commit e5ccfac19e

View File

@@ -27,101 +27,18 @@ pub use mutex_shim::{ShimMutex as Mutex, MutexGuard};
#[cfg(not(feature = "std"))]
pub use spin::Once as OnceLock;
#[rustversion::before(1.70)]
#[cfg(feature = "std")]
mod std_oncelock {
#[rustversion::before(1.70)]
mod before_1_70_oncelock {
use core::cell::Cell;
use std::sync::RwLock;
/// Shim for `std::sync::OnceLock`.
pub struct OnceLock<T> {
value: Cell<*mut T>,
init: RwLock<bool>,
}
// We use the `RwLock` (which is `Sync`) to control access to the `!Sync` `RefCell`
unsafe impl<T: Sync> Sync for OnceLock<T> {}
impl<T> OnceLock<T> {
/// Shim for `std::sync::OnceLock::new`.
pub const fn new() -> Self {
Self { value: Cell::new(core::ptr::null_mut()), init: RwLock::new(false) }
}
/// Shim for `std::sync::OnceLock::get_or_init`.
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
let initialized = *self.init.read().unwrap();
if !initialized {
// Obtain an exclusive reference
let mut initialized = self.init.write().unwrap();
// If this still isn't initialized (by someone who first obtained an exlusive reference)
if !*initialized {
// Set the value and mark it initialized
self.value.set(Box::into_raw(Box::new(f())));
*initialized = true;
}
}
// SAFETY: We always initialize the value before this and it's only written to once
unsafe { &*self.value.get() }
}
}
// SAFETY: `OnceLock` doesn't implement `Clone` so this doesn't risk dropping the `Box`
// multiple times
impl<T> Drop for OnceLock<T> {
fn drop(&mut self) {
if *self.init.read().unwrap() {
unsafe { drop(Box::from_raw(self.value.get())) }
}
}
}
}
#[rustversion::before(1.70)]
pub use before_1_70_oncelock::OnceLock;
#[rustversion::since(1.70)]
pub use std::sync::OnceLock;
}
pub use spin::Once as OnceLock;
#[rustversion::since(1.70)]
#[cfg(feature = "std")]
pub use std_oncelock::OnceLock;
pub use std::sync::OnceLock;
#[cfg(not(feature = "std"))]
pub use spin::Lazy as LazyLock;
#[rustversion::before(1.80)]
#[cfg(feature = "std")]
mod std_lazylock {
#[rustversion::before(1.80)]
mod before_1_80_lazylock {
use core::ops::Deref;
use crate::sync::{Mutex, OnceLock};
/// Shim for `std::sync::LazyLock`.
pub struct LazyLock<T, F = fn() -> T> {
f: Mutex<Option<F>>,
once: OnceLock<T>,
}
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// Shim for `std::sync::LazyLock::new`.
pub const fn new(f: F) -> Self {
Self { f: Mutex::new(Some(f)), once: OnceLock::new() }
}
/// Shim for `std::sync::LazyLock::get`.
pub fn get(&self) -> &T {
// Since this initializer will only be called once, the value in the Mutex will be `Some`
self.once.get_or_init(|| (self.f.lock().take().unwrap())())
}
}
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
type Target = T;
fn deref(&self) -> &T {
self.get()
}
}
}
#[rustversion::before(1.80)]
pub use before_1_80_lazylock::LazyLock;
#[rustversion::since(1.80)]
pub use std::sync::LazyLock;
}
pub use spin::Lazy as LazyLock;
#[rustversion::since(1.80)]
#[cfg(feature = "std")]
pub use std_lazylock::LazyLock;
pub use std::sync::LazyLock;