Make it so this crate can be `no_std` (#22)
This commit is contained in:
parent
60354f9ea4
commit
d3bf5a5424
|
@ -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
|
||||
|
|
|
@ -28,3 +28,7 @@ harness = false
|
|||
criterion = "0.3.4"
|
||||
easy-parallel = "3.1.0"
|
||||
fastrand = "1.3.3"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
36
src/lib.rs
36
src/lib.rs
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue