Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- `Lazy` is now `LazyLock`

### Fixed

# [0.10.0] - 2025-03-26
Expand Down
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ lock_api_crate = { package = "lock_api", version = "0.4", optional = true }
portable-atomic = { version = "1.3", optional = true, default-features = false, features = ["require-cas"] }

[features]
default = ["lock_api", "mutex", "spin_mutex", "rwlock", "once", "lazy", "barrier"]
default = ["lock_api", "mutex", "spin_mutex", "rwlock", "once", "lazylock", "barrier"]

# Enables `Mutex`. Must be used with either `spin_mutex` or `use_ticket_mutex`.
mutex = []
Expand All @@ -42,8 +42,11 @@ rwlock = []
# Enables `Once`.
once = []

# Enables `Lazy`.
lazy = ["once"]
# Enables `LazyLock`.
lazylock = ["once"]

# Enables `LazyLock`. This feature flag is here for compatibility reasons and may be dropped in future versions.
lazy = ["lazylock"]

# Enables `Barrier`. Because this feature uses `mutex`, either `spin_mutex` or `use_ticket_mutex` must be enabled.
barrier = ["mutex"]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ spinlocks. If you have access to `std`, it's likely that the primitives in

## Features

- `Mutex`, `RwLock`, `Once`, `Lazy` and `Barrier` equivalents
- `Mutex`, `RwLock`, `Once`, `LazyLock` and `Barrier` equivalents
- Support for `no_std` environments
- [`lock_api`](https://crates.io/crates/lock_api) compatibility
- Upgradeable `RwLock` guards
Expand Down Expand Up @@ -84,7 +84,7 @@ The crate comes with a few feature flags that you may wish to use.

- `once` enables the `Once` type.

- `lazy` enables the `Lazy` type.
- `lazylock` enables the `LazyLock` type.

- `barrier` enables the `Barrier` type.

Expand Down
36 changes: 18 additions & 18 deletions src/lazy.rs → src/lazylock.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
//! Synchronization primitives for lazy evaluation.
//!
//! Implementation adapted from the `SyncLazy` type of the standard library. See:
//! <https://doc.rust-lang.org/std/lazy/struct.SyncLazy.html>
//! Implementation adapted from the `LazyLock` type of the standard library. See:
//! <https://doc.rust-lang.org/std/sync/struct.LazyLock.html>

use crate::{once::Once, RelaxStrategy, Spin};
use core::{cell::Cell, fmt, ops::Deref};

/// A value which is initialized on the first access.
///
/// This type is a thread-safe `Lazy`, and can be used in statics.
/// This type is a thread-safe `LazyLock`, and can be used in statics.
///
/// # Examples
///
/// ```
/// use std::collections::HashMap;
/// use spin::Lazy;
/// use spin::LazyLock;
///
/// static HASHMAP: Lazy<HashMap<i32, String>> = Lazy::new(|| {
/// static HASHMAP: LazyLock<HashMap<i32, String>> = LazyLock::new(|| {
/// println!("initializing");
/// let mut m = HashMap::new();
/// m.insert(13, "Spica".to_string());
Expand All @@ -38,14 +38,14 @@ use core::{cell::Cell, fmt, ops::Deref};
/// // Some("Hoyten")
/// }
/// ```
pub struct Lazy<T, F = fn() -> T, R = Spin> {
pub struct LazyLock<T, F = fn() -> T, R = Spin> {
cell: Once<T, R>,
init: Cell<Option<F>>,
}

impl<T: fmt::Debug, F, R> fmt::Debug for Lazy<T, F, R> {
impl<T: fmt::Debug, F, R> fmt::Debug for LazyLock<T, F, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("Lazy");
let mut d = f.debug_tuple("LazyLock");
let d = if let Some(x) = self.cell.get() {
d.field(&x)
} else {
Expand All @@ -55,15 +55,15 @@ impl<T: fmt::Debug, F, R> fmt::Debug for Lazy<T, F, R> {
}
}

// We never create a `&F` from a `&Lazy<T, F>` so it is fine
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
// to not impl `Sync` for `F`
// we do create a `&mut Option<F>` in `force`, but this is
// properly synchronized, so it only happens once
// so it also does not contribute to this impl.
unsafe impl<T, F: Send> Sync for Lazy<T, F> where Once<T>: Sync {}
unsafe impl<T, F: Send> Sync for LazyLock<T, F> where Once<T>: Sync {}
// auto-derived `Send` impl is OK.

impl<T, F, R> Lazy<T, F, R> {
impl<T, F, R> LazyLock<T, F, R> {
/// Creates a new lazy value with the given initializing
/// function.
pub const fn new(f: F) -> Self {
Expand All @@ -82,38 +82,38 @@ impl<T, F, R> Lazy<T, F, R> {
}
}

impl<T, F: FnOnce() -> T, R: RelaxStrategy> Lazy<T, F, R> {
impl<T, F: FnOnce() -> T, R: RelaxStrategy> LazyLock<T, F, R> {
/// Forces the evaluation of this lazy value and
/// returns a reference to result. This is equivalent
/// to the `Deref` impl, but is explicit.
///
/// # Examples
///
/// ```
/// use spin::Lazy;
/// use spin::LazyLock;
///
/// let lazy = Lazy::new(|| 92);
/// let lazy = LazyLock::new(|| 92);
///
/// assert_eq!(Lazy::force(&lazy), &92);
/// assert_eq!(LazyLock::force(&lazy), &92);
/// assert_eq!(&*lazy, &92);
/// ```
pub fn force(this: &Self) -> &T {
this.cell.call_once(|| match this.init.take() {
Some(f) => f(),
None => panic!("Lazy instance has previously been poisoned"),
None => panic!("LazyLock instance has previously been poisoned"),
})
}
}

impl<T, F: FnOnce() -> T, R: RelaxStrategy> Deref for Lazy<T, F, R> {
impl<T, F: FnOnce() -> T, R: RelaxStrategy> Deref for LazyLock<T, F, R> {
type Target = T;

fn deref(&self) -> &T {
Self::force(self)
}
}

impl<T: Default, R> Default for Lazy<T, fn() -> T, R> {
impl<T: Default, R> Default for LazyLock<T, fn() -> T, R> {
/// Creates a new lazy value using `Default` as the initializing function.
fn default() -> Self {
Self::new(T::default)
Expand Down
25 changes: 16 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#![deny(missing_docs)]

//! This crate provides [spin-based](https://en.wikipedia.org/wiki/Spinlock) versions of the
//! primitives in `std::sync` and `std::lazy`. Because synchronization is done through spinning,
//! primitives in `std::sync`. Because synchronization is done through spinning,
//! the primitives are suitable for use in `no_std` environments.
//!
//! # Features
//!
//! - `Mutex`, `RwLock`, `Once`/`SyncOnceCell`, and `SyncLazy` equivalents
//! - `Mutex`, `RwLock`, `Once`/`SyncOnceCell`, and `LazyLock` equivalents
//!
//! - Support for `no_std` environments
//!
Expand Down Expand Up @@ -80,9 +80,9 @@ use portable_atomic as atomic;
#[cfg(feature = "barrier")]
#[cfg_attr(docsrs, doc(cfg(feature = "barrier")))]
pub mod barrier;
#[cfg(feature = "lazy")]
#[cfg_attr(docsrs, doc(cfg(feature = "lazy")))]
pub mod lazy;
#[cfg(feature = "lazylock")]
#[cfg_attr(docsrs, doc(cfg(feature = "lazylock")))]
pub mod lazylock;
#[cfg(feature = "mutex")]
#[cfg_attr(docsrs, doc(cfg(feature = "mutex")))]
pub mod mutex;
Expand Down Expand Up @@ -117,13 +117,20 @@ pub use rwlock::RwLockReadGuard;
#[cfg_attr(docsrs, doc(cfg(feature = "barrier")))]
pub type Barrier = crate::barrier::Barrier;

/// A value which is initialized on the first access. See [`lazy::Lazy`] for documentation.
/// A value which is initialized on the first access. See [`lazylock::LazyLock`] for documentation.
///
/// A note for advanced users: this alias exists to avoid subtle type inference errors due to the default relax
/// strategy type parameter. If you need a non-default relax strategy, use the fully-qualified path.
#[cfg(feature = "lazy")]
#[cfg_attr(docsrs, doc(cfg(feature = "lazy")))]
pub type Lazy<T, F = fn() -> T> = crate::lazy::Lazy<T, F>;
#[cfg(feature = "lazylock")]
#[cfg_attr(docsrs, doc(cfg(feature = "lazylock")))]
pub type LazyLock<T, F = fn() -> T> = crate::lazylock::LazyLock<T, F>;

/// A type alias to [`LazyLock`] for compatibility reasons.
///
#[deprecated(note = "use `spin::LazyLock` instead")]
#[cfg(feature = "lazylock")]
#[cfg_attr(docsrs, doc(cfg(feature = "lazylock")))]
pub type Lazy<T, F = fn() -> T> = crate::lazylock::LazyLock<T, F>;

/// A primitive that synchronizes the execution of multiple threads. See [`mutex::Mutex`] for documentation.
///
Expand Down