Skip to content

Commit

Permalink
Make this crate depend on NoUninit from bytemuck
Browse files Browse the repository at this point in the history
Fixes #36
Fixes #35
Fixes #23
  • Loading branch information
Amanieu committed Jul 29, 2023
1 parent 802ec51 commit f5333f1
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 33 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ version = "0.5.3"
edition = "2018"
authors = ["Amanieu d'Antras <amanieu@gmail.com>"]
description = "Generic Atomic<T> wrapper type"
documentation = "https://amanieu.github.io/atomic-rs/atomic/index.html"
license = "Apache-2.0/MIT"
repository = "https://github.com/Amanieu/atomic-rs"
readme = "README.md"
Expand All @@ -15,3 +14,9 @@ default = ["fallback"]
std = []
fallback = []
nightly = []

[dependencies]
bytemuck = "1.13.1"

[dev-dependencies]
bytemuck = { version = "1.13.1", features = ["derive"] }
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ Generic `Atomic<T>` for Rust

[![Build Status](https://travis-ci.org/Amanieu/atomic-rs.svg?branch=master)](https://travis-ci.org/Amanieu/atomic-rs) [![Crates.io](https://img.shields.io/crates/v/atomic.svg)](https://crates.io/crates/atomic)

A Rust library which provides a generic `Atomic<T>` type for all `T: Copy` types, unlike the standard library which only provides a few fixed atomic types (`AtomicBool`, `AtomicIsize`, `AtomicUsize`, `AtomicPtr`).
A Rust library which provides a generic `Atomic<T>` type for all `T: NoUninit` types, unlike the standard library which only provides a few fixed atomic types (`AtomicBool`, `AtomicIsize`, `AtomicUsize`, `AtomicPtr`). The `NoUninit` bound is from the [bytemuck] crate, and indicates that a type has no internal padding bytes. You will need to derive or implement this trait for all types used with `Atomic<T>`.

This library will use native atomic instructions if possible, and will otherwise fall back to a lock-based mechanism. You can use the `Atomic::<T>::is_lock_free()` function to check whether native atomic operations are supported for a given type. Note that a type must have a power-of-2 size and alignment in order to be used by native atomic instructions.

This crate uses `#![no_std]` and only depends on libcore.

[Documentation](https://amanieu.github.io/atomic-rs/atomic/index.html)
[bytemuck]: https://docs.rs/bytemuck

[Documentation](https://docs.rs/atomic)

## Usage

Expand Down
17 changes: 9 additions & 8 deletions src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

use core::cmp;
use core::hint;
use core::mem;
use core::num::Wrapping;
use core::ops;
use core::ptr;
use core::slice;
use core::sync::atomic::{AtomicUsize, Ordering};

use bytemuck::NoUninit;

// We use an AtomicUsize instead of an AtomicBool because it performs better
// on architectures that don't have byte-sized atomics.
//
Expand Down Expand Up @@ -117,15 +117,16 @@ pub unsafe fn atomic_swap<T>(dst: *mut T, val: T) -> T {
}

#[inline]
pub unsafe fn atomic_compare_exchange<T>(dst: *mut T, current: T, new: T) -> Result<T, T> {
pub unsafe fn atomic_compare_exchange<T: NoUninit>(
dst: *mut T,
current: T,
new: T,
) -> Result<T, T> {
let _l = lock(dst as usize);
let result = ptr::read(dst);
// compare_exchange compares with memcmp instead of Eq
let a = slice::from_raw_parts(&result as *const _ as *const u8, mem::size_of_val(&result));
let b = slice::from_raw_parts(
&current as *const _ as *const u8,
mem::size_of_val(&current),
);
let a = bytemuck::bytes_of(&result);
let b = bytemuck::bytes_of(&current);
if a == b {
ptr::write(dst, new);
Ok(result)
Expand Down
28 changes: 20 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
//! threads, and are the building blocks of other concurrent types.
//!
//! This library defines a generic atomic wrapper type `Atomic<T>` for all
//! `T: Copy` types.
//! `T: NoUninit` types.
//! Atomic types present operations that, when used correctly, synchronize
//! updates between threads.
//!
//! The `NoUninit` bound is from the [bytemuck] crate, and indicates that a
//! type has no internal padding bytes. You will need to derive or implement
//! this trait for all types used with `Atomic<T>`.
//!
//! Each method takes an `Ordering` which represents the strength of
//! the memory barrier for that operation. These orderings are the
//! same as [LLVM atomic orderings][1].
Expand All @@ -29,6 +33,8 @@
//! Most atomic types may be stored in static variables, initialized using
//! the `const fn` constructors. Atomic statics are often used for lazy global
//! initialization.
//!
//! [bytemuck]: https://docs.rs/bytemuck
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
Expand All @@ -49,6 +55,8 @@ use core::fmt;
#[cfg(feature = "std")]
use std::panic::RefUnwindSafe;

use bytemuck::NoUninit;

#[cfg(feature = "fallback")]
mod fallback;
mod ops;
Expand All @@ -71,16 +79,16 @@ unsafe impl<T: Copy + Send> Sync for Atomic<T> {}
// `Atomic` API does not allow doing any panic-inducing operation after writing
// to the target object.
#[cfg(feature = "std")]
impl<T: Copy + RefUnwindSafe> RefUnwindSafe for Atomic<T> {}
impl<T: RefUnwindSafe> RefUnwindSafe for Atomic<T> {}

impl<T: Copy + Default> Default for Atomic<T> {
impl<T: Default> Default for Atomic<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}

impl<T: Copy + fmt::Debug> fmt::Debug for Atomic<T> {
impl<T: NoUninit + fmt::Debug> fmt::Debug for Atomic<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Atomic")
.field(&self.load(Ordering::SeqCst))
Expand Down Expand Up @@ -108,7 +116,7 @@ impl<T> Atomic<T> {
}
}

impl<T: Copy> Atomic<T> {
impl<T: NoUninit> Atomic<T> {
#[inline]
fn inner_ptr(&self) -> *mut T {
self.v.get() as *mut T
Expand Down Expand Up @@ -392,13 +400,17 @@ atomic_ops_unsigned! { u8 u16 u32 u64 usize u128 }
#[cfg(test)]
mod tests {
use super::{Atomic, Ordering::*};
use bytemuck::NoUninit;
use core::mem;

#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, NoUninit)]
#[repr(C)]
struct Foo(u8, u8);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, NoUninit)]
#[repr(C)]
struct Bar(u64, u64);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, NoUninit)]
#[repr(C)]
struct Quux(u32);

#[test]
Expand Down
30 changes: 16 additions & 14 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use bytemuck::NoUninit;

#[cfg(feature = "fallback")]
use crate::fallback;
use core::cmp;
Expand Down Expand Up @@ -111,7 +113,7 @@ pub const fn atomic_is_lock_free<T>() -> bool {
}

#[inline]
pub unsafe fn atomic_load<T>(dst: *mut T, order: Ordering) -> T {
pub unsafe fn atomic_load<T: NoUninit>(dst: *mut T, order: Ordering) -> T {
match_atomic!(
T,
A,
Expand All @@ -121,7 +123,7 @@ pub unsafe fn atomic_load<T>(dst: *mut T, order: Ordering) -> T {
}

#[inline]
pub unsafe fn atomic_store<T>(dst: *mut T, val: T, order: Ordering) {
pub unsafe fn atomic_store<T: NoUninit>(dst: *mut T, val: T, order: Ordering) {
match_atomic!(
T,
A,
Expand All @@ -131,7 +133,7 @@ pub unsafe fn atomic_store<T>(dst: *mut T, val: T, order: Ordering) {
}

#[inline]
pub unsafe fn atomic_swap<T>(dst: *mut T, val: T, order: Ordering) -> T {
pub unsafe fn atomic_swap<T: NoUninit>(dst: *mut T, val: T, order: Ordering) -> T {
match_atomic!(
T,
A,
Expand All @@ -149,7 +151,7 @@ unsafe fn map_result<T, U>(r: Result<T, T>) -> Result<U, U> {
}

#[inline]
pub unsafe fn atomic_compare_exchange<T>(
pub unsafe fn atomic_compare_exchange<T: NoUninit>(
dst: *mut T,
current: T,
new: T,
Expand All @@ -170,7 +172,7 @@ pub unsafe fn atomic_compare_exchange<T>(
}

#[inline]
pub unsafe fn atomic_compare_exchange_weak<T>(
pub unsafe fn atomic_compare_exchange_weak<T: NoUninit>(
dst: *mut T,
current: T,
new: T,
Expand All @@ -191,7 +193,7 @@ pub unsafe fn atomic_compare_exchange_weak<T>(
}

#[inline]
pub unsafe fn atomic_add<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T
pub unsafe fn atomic_add<T: NoUninit>(dst: *mut T, val: T, order: Ordering) -> T
where
Wrapping<T>: ops::Add<Output = Wrapping<T>>,
{
Expand All @@ -204,7 +206,7 @@ where
}

#[inline]
pub unsafe fn atomic_sub<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T
pub unsafe fn atomic_sub<T: NoUninit>(dst: *mut T, val: T, order: Ordering) -> T
where
Wrapping<T>: ops::Sub<Output = Wrapping<T>>,
{
Expand All @@ -217,7 +219,7 @@ where
}

#[inline]
pub unsafe fn atomic_and<T: Copy + ops::BitAnd<Output = T>>(
pub unsafe fn atomic_and<T: NoUninit + ops::BitAnd<Output = T>>(
dst: *mut T,
val: T,
order: Ordering,
Expand All @@ -231,7 +233,7 @@ pub unsafe fn atomic_and<T: Copy + ops::BitAnd<Output = T>>(
}

#[inline]
pub unsafe fn atomic_or<T: Copy + ops::BitOr<Output = T>>(
pub unsafe fn atomic_or<T: NoUninit + ops::BitOr<Output = T>>(
dst: *mut T,
val: T,
order: Ordering,
Expand All @@ -245,7 +247,7 @@ pub unsafe fn atomic_or<T: Copy + ops::BitOr<Output = T>>(
}

#[inline]
pub unsafe fn atomic_xor<T: Copy + ops::BitXor<Output = T>>(
pub unsafe fn atomic_xor<T: NoUninit + ops::BitXor<Output = T>>(
dst: *mut T,
val: T,
order: Ordering,
Expand All @@ -259,7 +261,7 @@ pub unsafe fn atomic_xor<T: Copy + ops::BitXor<Output = T>>(
}

#[inline]
pub unsafe fn atomic_min<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
pub unsafe fn atomic_min<T: NoUninit + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
match_signed_atomic!(
T,
A,
Expand All @@ -269,7 +271,7 @@ pub unsafe fn atomic_min<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Orderin
}

#[inline]
pub unsafe fn atomic_max<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
pub unsafe fn atomic_max<T: NoUninit + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
match_signed_atomic!(
T,
A,
Expand All @@ -279,7 +281,7 @@ pub unsafe fn atomic_max<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Orderin
}

#[inline]
pub unsafe fn atomic_umin<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
pub unsafe fn atomic_umin<T: NoUninit + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
match_atomic!(
T,
A,
Expand All @@ -289,7 +291,7 @@ pub unsafe fn atomic_umin<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Orderi
}

#[inline]
pub unsafe fn atomic_umax<T: Copy + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
pub unsafe fn atomic_umax<T: NoUninit + cmp::Ord>(dst: *mut T, val: T, order: Ordering) -> T {
match_atomic!(
T,
A,
Expand Down

0 comments on commit f5333f1

Please sign in to comment.