Add a default "std" feature for no_std usage

This is a breaking change for no-default-features users.

Signed-off-by: John Nunley <dev@notgull.net>
This commit is contained in:
John Nunley 2023-08-16 22:10:58 -07:00
parent 8f8b4fea18
commit 3a82590a69
10 changed files with 135 additions and 104 deletions

View File

@ -38,13 +38,18 @@ jobs:
- name: Install Rust
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
- run: rustup target add wasm32-unknown-unknown
- name: Install WASM Test Tools
uses: taiki-e/install-action@wasm-pack
- name: Install WASM Test Tools and Cargo Hack
uses: taiki-e/install-action@v2
with:
tool: cargo-hack,wasm-pack
- name: Run cargo check
run: cargo check --all --all-features --all-targets
- run: cargo check --all --no-default-features
- name: Run cargo check (without dev-dependencies to catch missing feature flags)
if: startsWith(matrix.rust, 'nightly')
run: cargo check -Z features=dev_dep
- run: rustup target add thumbv7m-none-eabi
- run: cargo hack build --all --target thumbv7m-none-eabi --no-default-features --no-dev-deps
- name: Run cargo check for WASM
run: cargo check --all --all-features --all-targets --target wasm32-unknown-unknown
- name: Test WASM
@ -57,7 +62,7 @@ jobs:
matrix:
# When updating this, the reminder to update the minimum supported
# Rust version in Cargo.toml.
rust: ['1.48']
rust: ['1.59']
steps:
- uses: actions/checkout@v3
- name: Install Rust

View File

@ -6,7 +6,7 @@ name = "async-lock"
version = "2.7.0"
authors = ["Stjepan Glavina <stjepang@gmail.com>"]
edition = "2018"
rust-version = "1.48"
rust-version = "1.59"
description = "Async synchronization primitives"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/smol-rs/async-lock"
@ -15,10 +15,14 @@ categories = ["asynchronous", "concurrency"]
exclude = ["/.*"]
[dependencies]
event-listener = "2"
event-listener-strategy = { git = "https://github.com/smol-rs/event-listener.git" }
event-listener = { version = "3.0.0", default-features = false }
event-listener-strategy = { version = "0.2.0", default-features = false }
pin-project-lite = "0.2.11"
[features]
default = ["std"]
std = ["event-listener/std", "event-listener-strategy/std"]
[dev-dependencies]
async-channel = "1.5.0"
fastrand = "2.0.0"
@ -27,7 +31,3 @@ waker-fn = "1.1.0"
[target.'cfg(any(target_arch = "wasm32", target_arch = "wasm64"))'.dev-dependencies]
wasm-bindgen-test = "0.3"
[patch.crates-io]
async-channel = { git = "https://github.com/smol-rs/async-channel.git", branch = "notgull/evl-3.0" }
event-listener = { git = "https://github.com/smol-rs/event-listener.git" }

View File

@ -1,9 +1,9 @@
use event_listener::{Event, EventListener};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use crate::futures::Lock;
use crate::Mutex;
@ -148,7 +148,7 @@ impl Future for BarrierWait<'_> {
// We are the last one.
state.count = 0;
state.generation_id = state.generation_id.wrapping_add(1);
this.barrier.event.notify(std::usize::MAX);
this.barrier.event.notify(core::usize::MAX);
return Poll::Ready(BarrierWaitResult { is_leader: true });
}
}

View File

@ -7,6 +7,7 @@
//! * [`RwLock`] - a reader-writer lock, allowing any number of readers or a single writer.
//! * [`Semaphore`] - limits the number of concurrent operations.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![doc(
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
@ -15,6 +16,8 @@
html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
extern crate alloc;
/// Simple macro to extract the value of `Poll` or return `Pending`.
///
/// TODO: Drop in favor of `core::task::ready`, once MSRV is bumped to 1.64.
@ -38,7 +41,7 @@ macro_rules! pin {
let mut $x = $x;
#[allow(unused_mut)]
let mut $x = unsafe {
std::pin::Pin::new_unchecked(&mut $x)
core::pin::Pin::new_unchecked(&mut $x)
};
)*
}
@ -69,3 +72,25 @@ pub mod futures {
};
pub use crate::semaphore::{Acquire, AcquireArc};
}
#[cold]
fn abort() -> ! {
// For no_std targets, panicking while panicking is defined as an abort
#[cfg(not(feature = "std"))]
{
struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) {
panic!("Panicking while panicking to abort")
}
}
let _bomb = Bomb;
panic!("Panicking while panicking to abort")
}
// For libstd targets, abort using std::process::abort
#[cfg(feature = "std")]
std::process::abort()
}

View File

@ -1,20 +1,18 @@
use std::borrow::Borrow;
use std::cell::UnsafeCell;
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::Poll;
use core::borrow::Borrow;
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::task::Poll;
use core::usize;
// Note: we cannot use `target_family = "wasm"` here because it requires Rust 1.54.
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use alloc::sync::Arc;
#[cfg(all(feature = "std", not(target_family = "wasm")))]
use std::time::{Duration, Instant};
use std::usize;
use event_listener::{Event, EventListener};
use event_listener_strategy::{easy_wrapper, EventListenerFuture};
@ -263,6 +261,7 @@ impl<T: Default + ?Sized> Default for Mutex<T> {
easy_wrapper! {
/// The future returned by [`Mutex::lock`].
pub struct Lock<'a, T: ?Sized>(LockInner<'a, T> => MutexGuard<'a, T>);
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub(crate) wait();
}
@ -320,6 +319,7 @@ impl<'a, T: ?Sized> EventListenerFuture for LockInner<'a, T> {
easy_wrapper! {
/// The future returned by [`Mutex::lock_arc`].
pub struct LockArc<T: ?Sized>(LockArcInnards<T> => MutexGuardArc<T>);
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub(crate) wait();
}
@ -412,7 +412,7 @@ pin_project_lite::pin_project! {
/// `pin_project_lite` doesn't support `#[cfg]` yet, so we have to do this manually.
struct Start {
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[cfg(all(feature = "std", not(target_family = "wasm")))]
start: Option<Instant>,
}
@ -430,7 +430,7 @@ impl<T: ?Sized, B: Borrow<Mutex<T>>> AcquireSlow<B, T> {
mutex: Some(mutex),
listener,
start: Start {
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[cfg(all(feature = "std", not(target_family = "wasm")))]
start: None,
},
starved: false,
@ -464,7 +464,7 @@ impl<T: ?Sized, B: Unpin + Borrow<Mutex<T>>> EventListenerFuture for AcquireSlow
context: &mut S::Context,
) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[cfg(all(feature = "std", not(target_family = "wasm")))]
let start = *this.start.start.get_or_insert_with(Instant::now);
let mutex = Borrow::<Mutex<T>>::borrow(
this.mutex.as_ref().expect("future polled after completion"),
@ -518,7 +518,7 @@ impl<T: ?Sized, B: Unpin + Borrow<Mutex<T>>> EventListenerFuture for AcquireSlow
// If waiting for too long, fall back to a fairer locking strategy that will prevent
// newer lock operations from starving us forever.
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[cfg(all(feature = "std", not(target_family = "wasm")))]
if start.elapsed() > Duration::from_micros(500) {
break;
}
@ -528,7 +528,7 @@ impl<T: ?Sized, B: Unpin + Borrow<Mutex<T>>> EventListenerFuture for AcquireSlow
// Increment the number of starved lock operations.
if mutex.state.fetch_add(2, Ordering::Release) > usize::MAX / 2 {
// In case of potential overflow, abort.
process::abort();
crate::abort();
}
// Indicate that we are now starving and will use a fairer locking strategy.

View File

@ -1,14 +1,16 @@
use std::cell::UnsafeCell;
use std::convert::Infallible;
use std::fmt;
use std::future::Future;
use std::mem::{forget, MaybeUninit};
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use core::cell::UnsafeCell;
use core::convert::Infallible;
use core::fmt;
use core::future::Future;
use core::mem::{forget, MaybeUninit};
use core::ptr;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(all(feature = "std", not(target_family = "wasm")))]
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use event_listener::{Event, EventListener};
use event_listener_strategy::{Blocking, NonBlocking, Strategy};
use event_listener_strategy::{NonBlocking, Strategy};
/// The current state of the `OnceCell`.
#[derive(Copy, Clone, PartialEq, Eq)]
@ -319,6 +321,7 @@ impl<T> OnceCell<T> {
///
/// assert_eq!(cell.wait_blocking(), &1);
/// ```
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub fn wait_blocking(&self) -> &T {
// Fast path: see if the value is already initialized.
if let Some(value) = self.get() {
@ -423,6 +426,7 @@ impl<T> OnceCell<T> {
///
/// assert_eq!(result.unwrap(), &1);
/// ```
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub fn get_or_try_init_blocking<E>(
&self,
closure: impl FnOnce() -> Result<T, E>,
@ -435,8 +439,8 @@ impl<T> OnceCell<T> {
// Slow path: initialize the value.
// The futures provided should never block, so we can use `now_or_never`.
now_or_never(self.initialize_or_wait(
move || std::future::ready(closure()),
&mut Blocking::default(),
move || core::future::ready(closure()),
&mut event_listener_strategy::Blocking::default(),
))?;
debug_assert!(self.is_initialized());
@ -497,6 +501,7 @@ impl<T> OnceCell<T> {
/// assert_eq!(cell.get_or_init_blocking(|| 1), &1);
/// assert_eq!(cell.get_or_init_blocking(|| 2), &1);
/// ```
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub fn get_or_init_blocking(&self, closure: impl FnOnce() -> T + Unpin) -> &T {
match self.get_or_try_init_blocking(move || {
let result: Result<T, Infallible> = Ok(closure());
@ -563,6 +568,7 @@ impl<T> OnceCell<T> {
/// assert_eq!(cell.get(), Some(&1));
/// assert_eq!(cell.set_blocking(2), Err(2));
/// ```
#[cfg(all(feature = "std", not(target_family = "wasm")))]
pub fn set_blocking(&self, value: T) -> Result<&T, T> {
let mut value = Some(value);
self.get_or_init_blocking(|| value.take().unwrap());
@ -643,8 +649,8 @@ impl<T> OnceCell<T> {
.store(State::Initialized.into(), Ordering::Release);
// Notify the listeners that the value is initialized.
self.active_initializers.notify_additional(std::usize::MAX);
self.passive_waiters.notify_additional(std::usize::MAX);
self.active_initializers.notify_additional(core::usize::MAX);
self.passive_waiters.notify_additional(core::usize::MAX);
return Ok(());
}
@ -758,6 +764,7 @@ impl<T> Drop for OnceCell<T> {
}
/// Either return the result of a future now, or panic.
#[cfg(all(feature = "std", not(target_family = "wasm")))]
fn now_or_never<T>(f: impl Future<Output = T>) -> T {
const NOOP_WAKER: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);

View File

@ -1,9 +1,10 @@
use std::cell::UnsafeCell;
use std::fmt;
use std::mem::{self, ManuallyDrop};
use std::ops::{Deref, DerefMut};
use std::ptr::{self, NonNull};
use std::sync::Arc;
use core::cell::UnsafeCell;
use core::fmt;
use core::mem::{self, ManuallyDrop};
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull};
use alloc::sync::Arc;
pub(crate) mod futures;
mod raw;

View File

@ -1,9 +1,10 @@
use std::fmt;
use std::future::Future;
use std::mem::ManuallyDrop;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use core::fmt;
use core::future::Future;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::task::{Context, Poll};
use alloc::sync::Arc;
use super::raw::{RawRead, RawUpgradableRead, RawUpgrade, RawWrite};
use super::{

View File

@ -6,12 +6,11 @@
//! the locking code only once, and also lets us make
//! [`RwLockReadGuard`](super::RwLockReadGuard) covariant in `T`.
use std::future::Future;
use std::mem::forget;
use std::pin::Pin;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Context, Poll};
use core::future::Future;
use core::mem::forget;
use core::pin::Pin;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::task::{Context, Poll};
use event_listener::{Event, EventListener};
@ -66,8 +65,8 @@ impl RawRwLock {
}
// Make sure the number of readers doesn't overflow.
if state > std::isize::MAX as usize {
process::abort();
if state > core::isize::MAX as usize {
crate::abort();
}
// Increment the number of readers.
@ -107,8 +106,8 @@ impl RawRwLock {
let mut state = self.state.load(Ordering::Acquire);
// Make sure the number of readers doesn't overflow.
if state > std::isize::MAX as usize {
process::abort();
if state > core::isize::MAX as usize {
crate::abort();
}
// Increment the number of readers.
@ -308,8 +307,8 @@ impl<'a> Future for RawRead<'a> {
loop {
if *this.state & WRITER_BIT == 0 {
// Make sure the number of readers doesn't overflow.
if *this.state > std::isize::MAX as usize {
process::abort();
if *this.state > core::isize::MAX as usize {
crate::abort();
}
// If nobody is holding a write lock or attempting to acquire it, increment the
@ -375,8 +374,8 @@ impl<'a> Future for RawUpgradableRead<'a> {
let mut state = this.lock.state.load(Ordering::Acquire);
// Make sure the number of readers doesn't overflow.
if state > std::isize::MAX as usize {
process::abort();
if state > core::isize::MAX as usize {
crate::abort();
}
// Increment the number of readers.

View File

@ -1,9 +1,10 @@
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::task::{Context, Poll};
use alloc::sync::Arc;
use event_listener::{Event, EventListener};
@ -147,7 +148,7 @@ impl Semaphore {
pub fn acquire_arc(self: &Arc<Self>) -> AcquireArc {
AcquireArc {
semaphore: self.clone(),
listener: None,
listener: EventListener::new(&self.event),
}
}
@ -216,15 +217,16 @@ impl<'a> Future for Acquire<'a> {
}
}
/// The future returned by [`Semaphore::acquire_arc`].
pub struct AcquireArc {
/// The semaphore being acquired.
semaphore: Arc<Semaphore>,
pin_project_lite::pin_project! {
/// The future returned by [`Semaphore::acquire_arc`].
pub struct AcquireArc {
// The semaphore being acquired.
semaphore: Arc<Semaphore>,
/// The listener waiting on the semaphore.
///
/// TODO: At the next breaking release, remove the `Pin<Box<>>` and make this type `!Unpin`.
listener: Option<Pin<Box<EventListener>>>,
// The listener waiting on the semaphore.
#[pin]
listener: EventListener,
}
}
impl fmt::Debug for AcquireArc {
@ -233,30 +235,21 @@ impl fmt::Debug for AcquireArc {
}
}
impl Unpin for AcquireArc {}
impl Future for AcquireArc {
type Output = SemaphoreGuardArc;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut this = self.project();
loop {
match this.semaphore.try_acquire_arc() {
Some(guard) => {
this.listener = None;
return Poll::Ready(guard);
}
Some(guard) => return Poll::Ready(guard),
None => {
// Wait on the listener.
match &mut this.listener {
None => {
this.listener = Some(this.semaphore.event.listen());
}
Some(ref mut listener) => {
ready!(Pin::new(listener).poll(cx));
this.listener = None;
}
if !this.listener.is_listening() {
this.listener.as_mut().listen();
} else {
ready!(this.listener.as_mut().poll(cx));
}
}
}