Skip to content

Commit

Permalink
Make the lock_api dependency optional. (#11)
Browse files Browse the repository at this point in the history
* Make the `lock_api` dependency optional.

Without `lock_api`, this crate only provides a small number of types
such as `RawMutex` and `RawRwLock`, but if someone needs just those,
it's reasonable to provide them without depending on `lock_api`.

* Change `RawCondvar` to a `pub use`.

Using a `pub use` instead of a `pub type` alias gives it better
rustdoc documentation.
  • Loading branch information
sunfishcode authored Jun 12, 2024
1 parent 0c6475b commit 884a549
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 7 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,7 @@ jobs:
cargo test
env:
RUST_BACKTRACE: 1

- name: cargo check --no-default-features
run: |
cargo check --no-default-features
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ rust-version = "1.70"

[dependencies]
rustix = { version = "0.38.34", default-features = false, features = ["thread", "time"] }
lock_api = { version = "0.4.7", default-features = false }
lock_api = { version = "0.4.7", default-features = false, optional = true }

# Special dependencies used in rustc-dep-of-std mode.
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
Expand All @@ -26,8 +26,9 @@ compiler_builtins = { version = "0.1.101", optional = true }
rand = "0.8.5"

[features]
nightly = ["lock_api/nightly"]
atomic_usize = ["lock_api/atomic_usize"]
default = ["lock_api"]
nightly = ["lock_api?/nightly"]
atomic_usize = ["lock_api?/atomic_usize"]

rustc-dep-of-std = [
"dep:core",
Expand Down
2 changes: 1 addition & 1 deletion src/futex_condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
use super::wait_wake::{futex_wait, futex_wake, futex_wake_all};
use core::time::Duration;
use super::RawMutex;
use lock_api::RawMutex as _;
use super::lock_api::RawMutex as _;

#[repr(transparent)]
pub struct Condvar {
Expand Down
20 changes: 17 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,29 @@
#![cfg_attr(doc_cfg, feature(doc_cfg))]

// Re-export this so that our users can use the same version we do.
#[cfg(feature = "lock_api")]
pub use lock_api;

// If we don't have the real `lock_api` crate, use our polyfills.
#[cfg(not(feature = "lock_api"))]
pub mod lock_api;

// Export convenient `Mutex` and `RwLock` types.
#[cfg(feature = "lock_api")]
pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;
#[cfg(feature = "lock_api")]
pub type RwLock<T> = lock_api::RwLock<RawRwLock, T>;
#[cfg(feature = "lock_api")]
pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>;
#[cfg(feature = "lock_api")]
pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>;
#[cfg(feature = "lock_api")]
pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>;
#[cfg(feature = "lock_api")]
#[cfg(feature = "atomic_usize")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "atomic_usize")))]
pub type ReentrantMutex<G, T> = lock_api::ReentrantMutex<RawMutex, G, T>;
#[cfg(feature = "lock_api")]
#[cfg(feature = "atomic_usize")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "atomic_usize")))]
pub type ReentrantMutexGuard<'a, G, T> = lock_api::ReentrantMutexGuard<'a, RawMutex, G, T>;
Expand All @@ -23,12 +35,14 @@ pub use once::{Once, OnceState};
pub use once_lock::OnceLock;

// Export the condvar types.
#[cfg(feature = "lock_api")]
pub use condvar::{Condvar, WaitTimeoutResult};

// Export the raw condvar types.
pub type RawCondvar = futex_condvar::Condvar;
pub use futex_condvar::Condvar as RawCondvar;

// std's implementation code.
#[cfg(feature = "lock_api")]
mod condvar;
mod futex_condvar;
mod futex_mutex;
Expand All @@ -41,7 +55,7 @@ mod wait_wake;
/// An implementation of [`lock_api::RawMutex`].
///
/// All of this `RawMutex`'s methods are in its implementation of
/// [`lock_api::RawMutex]`]. To import that trait without conflicting
/// [`lock_api::RawMutex`]. To import that trait without conflicting
/// with this `RawMutex` type, use:
///
/// ```
Expand All @@ -53,7 +67,7 @@ pub struct RawMutex(futex_mutex::Mutex);
/// An implementation of [`lock_api::RawRwLock`].
///
/// All of this `RawRwLock`'s methods are in its implementation of
/// [`lock_api::RawRwLock]`]. To import that trait without conflicting
/// [`lock_api::RawRwLock`]. To import that trait without conflicting
/// with this `RawRwLock` type, use:
///
/// ```
Expand Down
68 changes: 68 additions & 0 deletions src/lock_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! Polyfills for `lock_api` traits for when we don't have the real `lock_api`
//! crate.
/// Polyfill for [`lock_api::GuardNoSend`].
///
/// [`lock_api::GuardNoSend`]: https://docs.rs/lock_api/*/lock_api/struct.GuardNoSend.html
pub struct GuardNoSend(());

/// Polyfill for [`lock_api::RawMutex`].
///
/// [`lock_api::RawMutex`]: https://docs.rs/lock_api/*/lock_api/trait.RawMutex.html
pub unsafe trait RawMutex {
type GuardMarker;

const INIT: Self;

fn lock(&self);
fn try_lock(&self) -> bool;
unsafe fn unlock(&self);

#[inline]
fn is_locked(&self) -> bool {
let acquired_lock = self.try_lock();
if acquired_lock {
unsafe {
self.unlock();
}
}
!acquired_lock
}
}

/// Polyfill for [`lock_api::RawRwLock`].
///
/// [`lock_api::RawRwLock`]: https://docs.rs/lock_api/*/lock_api/trait.RawRwLock.html
pub unsafe trait RawRwLock {
type GuardMarker;

const INIT: Self;

fn lock_shared(&self);
fn try_lock_shared(&self) -> bool;
unsafe fn unlock_shared(&self);
fn lock_exclusive(&self);
fn try_lock_exclusive(&self) -> bool;
unsafe fn unlock_exclusive(&self);

#[inline]
fn is_locked(&self) -> bool {
let acquired_lock = self.try_lock_exclusive();
if acquired_lock {
unsafe {
self.unlock_exclusive();
}
}
!acquired_lock
}

fn is_locked_exclusive(&self) -> bool {
let acquired_lock = self.try_lock_shared();
if acquired_lock {
unsafe {
self.unlock_shared();
}
}
!acquired_lock
}
}

0 comments on commit 884a549

Please sign in to comment.