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
90 changes: 90 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
check:
name: Check (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
run: rustup update stable

- name: Make sure necessary tools are installed
run: rustup component add clippy --toolchain stable

- name: Setup rust-cache
uses: Swatinem/rust-cache@v2
with:
cache-bin: false

- name: Run cargo clippy
run: cargo clippy --all-targets --all-features -- -D warnings

- name: Check formatting (cargo fmt)
run: cargo fmt --all --check

test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
run: rustup update stable

- name: Setup rust-cache
uses: Swatinem/rust-cache@v2
with:
cache-bin: false

- name: Run cargo test
run: cargo test --all-features

toml:
name: Format (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
run: rustup update stable

- name: Setup rust-cache
uses: Swatinem/rust-cache@v2
with:
cache-bin: false

- name: Install latest cargo-machete
uses: taiki-e/install-action@cargo-machete

- name: Install latest taplo
uses: taiki-e/install-action@taplo

- name: Check unused dependencies (cargo machete)
run: cargo machete

- name: Check TOML formatting (taplo)
if: ${{ !contains(matrix.os, 'windows') }}
run: taplo fmt --check --diff
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
description = "A fast thread-safe single producer-single consumer ring buffer"
repository = "https://github.com/Mallets/ringbuffer-spsc"
license = "EPL-2.0 OR Apache-2.0"
rust-version = "1.79"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
[![CI](https://github.com/Mallets/ringbuffer-spsc/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Mallets/ringbuffer-spsc/actions?query=workflow%3ACI+branch%3Amain++)
[![docs.rs](https://img.shields.io/docsrs/ringbuffer-spsc)](https://docs.rs/ringbuffer-spsc/latest/ringbuffer_spsc/)
[![License](https://img.shields.io/badge/License-EPL%202.0-blue)](https://choosealicense.com/licenses/epl-2.0/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

# ringbuffer-spsc

A fast single-producer single-consumer ring buffer.
Expand Down Expand Up @@ -48,7 +53,7 @@ fn main() {
Tests run on an Apple M4, 32 GB of RAM.

```sh
$ cargo run --release --example throughput
cargo run --release --example throughput
```

Provides `~520M elem/s` of sustained throughput.
Expand All @@ -65,4 +70,4 @@ Provides `~520M elem/s` of sustained throughput.
520486274 elem/s
522570696 elem/s
...
```
```
10 changes: 6 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
//!
//! # Example
//! ```rust
//! use ringbuffer_spsc::RingBuffer;
//! use ringbuffer_spsc::ringbuffer;
//!
//! const N: usize = 1_000_000;
//! let (mut tx, mut rx) = RingBuffer::<usize>::new(16);
//! let (mut tx, mut rx) = ringbuffer::<usize>(16);
//!
//! let p = std::thread::spawn(move || {
//! let mut current: usize = 0;
Expand Down Expand Up @@ -49,17 +49,18 @@ use crossbeam_utils::CachePadded;
/// Panic: it panics if capacity is not a power of 2.
pub fn ringbuffer<T>(capacity: usize) -> (RingBufferWriter<T>, RingBufferReader<T>) {
assert!(capacity.is_power_of_two(), "Capacity must be a power of 2");

// Inner container
let v = (0..capacity)
.map(|_| MaybeUninit::uninit())
.collect::<Vec<_>>()
.into_boxed_slice();

let rb = Arc::new(RingBuffer {
// Keep
// Keep the pointer to the boxed slice
ptr: Box::into_raw(v),
// Since capacity is a power of two, capacity-1 is a mask covering N elements overflowing when N elements have been added.
// Indexes are left growing indefinetely and naturally wraps around once the index increment reaches usize::MAX.
// Indexes are left growing indefinetely and naturally wrap around once the index increment reaches usize::MAX.
mask: capacity - 1,
idx_r: CachePadded::new(AtomicUsize::new(0)),
idx_w: CachePadded::new(AtomicUsize::new(0)),
Expand All @@ -86,6 +87,7 @@ struct RingBuffer<T> {
}

impl<T> RingBuffer<T> {
#[allow(clippy::mut_from_ref)]
unsafe fn get_unchecked_mut(&self, idx: usize) -> &mut MaybeUninit<T> {
unsafe { (&mut (*self.ptr)).get_unchecked_mut(idx & self.mask) }
}
Expand Down
Loading