Make it so this crate can be `no_std` (#22)

This commit is contained in:
John Nunley 2022-08-24 10:09:13 -07:00 committed by GitHub
parent 60354f9ea4
commit d3bf5a5424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 26 deletions

View File

@ -29,6 +29,10 @@ jobs:
if: startsWith(matrix.rust, 'nightly')
run: cargo check -Z features=dev_dep
- run: cargo test
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- run: rustup target add thumbv7m-none-eabi
- run: cargo hack build --target thumbv7m-none-eabi --no-default-features --no-dev-deps
msrv:
runs-on: ubuntu-latest
@ -42,6 +46,10 @@ jobs:
- name: Install Rust
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
- run: cargo build
- name: Install cargo-hack
uses: taiki-e/install-action@cargo-hack
- run: rustup target add thumbv7m-none-eabi
- run: cargo hack build --target thumbv7m-none-eabi --no-default-features --no-dev-deps
clippy:
runs-on: ubuntu-latest

View File

@ -28,3 +28,7 @@ harness = false
criterion = "0.3.4"
easy-parallel = "3.1.0"
fastrand = "1.3.3"
[features]
default = ["std"]
std = []

View File

@ -1,11 +1,11 @@
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use alloc::{boxed::Box, vec::Vec};
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicUsize, Ordering};
use cache_padded::CachePadded;
use crate::{PopError, PushError};
use crate::{busy_wait, PopError, PushError};
/// A slot in a queue.
struct Slot<T> {
@ -141,7 +141,7 @@ impl<T> Bounded<T> {
tail = self.tail.load(Ordering::Relaxed);
} else {
// Yield because we need to wait for the stamp to get updated.
thread::yield_now();
busy_wait();
tail = self.tail.load(Ordering::Relaxed);
}
}
@ -207,7 +207,7 @@ impl<T> Bounded<T> {
head = self.head.load(Ordering::Relaxed);
} else {
// Yield because we need to wait for the stamp to get updated.
thread::yield_now();
busy_wait();
head = self.head.load(Ordering::Relaxed);
}
}

View File

@ -24,16 +24,32 @@
//! assert_eq!(q.pop(), Ok(2));
//! ```
//!
//! # Features
//!
//! `concurrent-queue` used an `std` default feature. With this feature enabled, this crate will
//! use [`std::thread::yield_now`] to avoid busy waiting in tight loops. However, with this
//! feature disabled, [`core::hint::spin_loop`] will be used instead. Disabling `std` will allow
//! this crate to be used on `no_std` platforms at the potential expense of more busy waiting.
//!
//! [Bounded]: `ConcurrentQueue::bounded()`
//! [Unbounded]: `ConcurrentQueue::unbounded()`
//! [closed]: `ConcurrentQueue::close()`
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::boxed::Box;
use core::fmt;
use core::sync::atomic::{self, AtomicUsize, Ordering};
#[cfg(feature = "std")]
use std::error;
use std::fmt;
#[cfg(feature = "std")]
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::atomic::{self, AtomicUsize, Ordering};
use crate::bounded::Bounded;
use crate::single::Single;
@ -65,7 +81,9 @@ pub struct ConcurrentQueue<T>(Inner<T>);
unsafe impl<T: Send> Send for ConcurrentQueue<T> {}
unsafe impl<T: Send> Sync for ConcurrentQueue<T> {}
#[cfg(feature = "std")]
impl<T> UnwindSafe for ConcurrentQueue<T> {}
#[cfg(feature = "std")]
impl<T> RefUnwindSafe for ConcurrentQueue<T> {}
enum Inner<T> {
@ -367,6 +385,7 @@ impl PopError {
}
}
#[cfg(feature = "std")]
impl error::Error for PopError {}
impl fmt::Debug for PopError {
@ -423,6 +442,7 @@ impl<T> PushError<T> {
}
}
#[cfg(feature = "std")]
impl<T: fmt::Debug> error::Error for PushError<T> {}
impl<T: fmt::Debug> fmt::Debug for PushError<T> {
@ -443,6 +463,18 @@ impl<T> fmt::Display for PushError<T> {
}
}
/// Notify the CPU that we are currently busy-waiting.
#[inline]
fn busy_wait() {
#[cfg(feature = "std")]
std::thread::yield_now();
// Use the deprecated `spin_loop_hint` here in order to
// avoid bumping the MSRV.
#[allow(deprecated)]
#[cfg(not(feature = "std"))]
core::sync::atomic::spin_loop_hint()
}
/// Equivalent to `atomic::fence(Ordering::SeqCst)`, but in some cases faster.
#[inline]
fn full_fence() {

View File

@ -1,9 +1,8 @@
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::{PopError, PushError};
use crate::{busy_wait, PopError, PushError};
const LOCKED: usize = 1 << 0;
const PUSHED: usize = 1 << 1;
@ -77,7 +76,7 @@ impl<T> Single<T> {
if prev & LOCKED == 0 {
state = prev;
} else {
thread::yield_now();
busy_wait();
state = prev & !LOCKED;
}
}

View File

@ -1,12 +1,12 @@
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use std::thread;
use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use cache_padded::CachePadded;
use crate::{PopError, PushError};
use crate::{busy_wait, PopError, PushError};
// Bits indicating the state of a slot:
// * If a value has been written into the slot, `WRITE` is set.
@ -45,7 +45,7 @@ impl<T> Slot<T> {
/// Waits until a value is written into the slot.
fn wait_write(&self) {
while self.state.load(Ordering::Acquire) & WRITE == 0 {
thread::yield_now();
busy_wait();
}
}
}
@ -77,7 +77,7 @@ impl<T> Block<T> {
if !next.is_null() {
return next;
}
thread::yield_now();
busy_wait();
}
}
@ -152,7 +152,7 @@ impl<T> Unbounded<T> {
// If we reached the end of the block, wait until the next one is installed.
if offset == BLOCK_CAP {
thread::yield_now();
busy_wait();
tail = self.tail.index.load(Ordering::Acquire);
block = self.tail.block.load(Ordering::Acquire);
continue;
@ -228,7 +228,7 @@ impl<T> Unbounded<T> {
// If we reached the end of the block, wait until the next one is installed.
if offset == BLOCK_CAP {
thread::yield_now();
busy_wait();
head = self.head.index.load(Ordering::Acquire);
block = self.head.block.load(Ordering::Acquire);
continue;
@ -258,7 +258,7 @@ impl<T> Unbounded<T> {
// The block can be null here only if the first push operation is in progress.
if block.is_null() {
thread::yield_now();
busy_wait();
head = self.head.index.load(Ordering::Acquire);
block = self.head.block.load(Ordering::Acquire);
continue;