Created
January 24, 2025 11:33
-
-
Save cr0sh/e5408a5d74366ae44bf1b19edc252363 to your computer and use it in GitHub Desktop.
Rust std LazyLock ported to parking-lot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mod lazy_lock { | |
| use parking_lot::{Once, OnceState}; | |
| use std::{cell::UnsafeCell, default::Default, mem::ManuallyDrop, ops::Deref}; | |
| union Data<T, F> { | |
| value: ManuallyDrop<T>, | |
| f: ManuallyDrop<F>, | |
| } | |
| pub struct LazyLock<T, F = fn() -> T> { | |
| once: Once, | |
| data: UnsafeCell<Data<T, F>>, | |
| } | |
| impl<T: Default> Default for LazyLock<T> { | |
| fn default() -> Self { | |
| Self::new(Default::default) | |
| } | |
| } | |
| impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |
| /// Creates a new lazy value. with the given initializing function. | |
| #[inline] | |
| pub const fn new(f: F) -> LazyLock<T, F> { | |
| LazyLock { | |
| once: Once::new(), | |
| data: UnsafeCell::new(Data { | |
| f: ManuallyDrop::new(f), | |
| }), | |
| } | |
| } | |
| /// Returns a reference to the value if initialized, or `None` if not. | |
| #[inline] | |
| pub fn get(this: &LazyLock<T, F>) -> Option<&T> { | |
| if this.once.state() == OnceState::Done { | |
| // SAFETY: | |
| // The closure has been run successfully, so `value` has been initialized | |
| // and will not be modified again. | |
| Some(unsafe { &(*this.data.get()).value }) | |
| } else { | |
| None | |
| } | |
| } | |
| /// Forces the evaluation of this lazy value and returns a reference to | |
| /// result. This is equivalent to the `Deref` impl, but is explicit. | |
| /// | |
| /// This method will block the calling thread if another initialization | |
| /// routine is currently running. | |
| #[inline] | |
| pub fn force(this: &LazyLock<T, F>) -> &T { | |
| this.once.call_once(|| { | |
| // SAFETY: `call_once` only runs this closure once, ever. | |
| let data = unsafe { &mut *this.data.get() }; | |
| let f = unsafe { ManuallyDrop::take(&mut data.f) }; | |
| let value = f(); | |
| data.value = ManuallyDrop::new(value); | |
| }); | |
| // SAFETY: | |
| // There are four possible scenarios: | |
| // * the closure was called and initialized `value`. | |
| // * the closure was called and panicked, so this point is never reached. | |
| // * the closure was not called, but a previous call initialized `value`. | |
| // * the closure was not called because the Once is poisoned, so this point | |
| // is never reached. | |
| // So `value` has definitely been initialized and will not be modified again. | |
| unsafe { &(*this.data.get()).value } | |
| } | |
| } | |
| impl<T, F> Drop for LazyLock<T, F> { | |
| fn drop(&mut self) { | |
| match self.once.state() { | |
| OnceState::New => {} | |
| OnceState::InProgress => unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) }, | |
| OnceState::Done => unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) }, | |
| OnceState::Poisoned => {} | |
| } | |
| } | |
| } | |
| impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> { | |
| type Target = T; | |
| /// Dereferences the value. | |
| /// | |
| /// This method will block the calling thread if another initialization | |
| /// routine is currently running. | |
| #[inline] | |
| fn deref(&self) -> &T { | |
| LazyLock::force(self) | |
| } | |
| } | |
| unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {} | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment