Skip to content

Commit

Permalink
Changed atomic bitflags to be handled by generic wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
f-gagel committed Jan 20, 2024
1 parent 8b478ac commit 0efb392
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 153 deletions.
161 changes: 143 additions & 18 deletions src/atomic.rs
Expand Up @@ -3,26 +3,151 @@
//! The code here belongs to the end-user, so new trait implementations and methods can't be
//! added without potentially breaking users.

#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __declare_atomic_bitflags {
(
$(#[$outer:meta])*
$vis:vis struct $AtomicBitFlags:ident : $Flags:ident
) => {
$(#[$outer])*
$vis struct $AtomicBitFlags(<<$Flags as $crate::Flags>::Bits as $crate::HasAtomic>::Atomic);

impl $crate::AtomicFlags for $AtomicBitFlags {
type Flags = $Flags;
type AtomicBits = <<$Flags as $crate::Flags>::Bits as $crate::HasAtomic>::Atomic;

fn atomic_bits(&self) -> &Self::AtomicBits {
&self.0
use core::sync::atomic::*;
use crate::{Bits, Flags};

/// Bits type that has an atomic variant
pub trait HasAtomic : Bits {
type Atomic: AtomicBits<Bits=Self>;
}

/// A type that can be used as atomic storage for a flags type
pub trait AtomicBits :
From<Self::Bits>
+ Sized
+ 'static
{
type Bits: HasAtomic<Atomic=Self>;

fn fetch_and(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn fetch_or(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn fetch_xor(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn load(&self, order: Ordering) -> Self::Bits;
fn store(&self, val: Self::Bits, order: Ordering);
fn swap(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
}


macro_rules! impl_atomic {
($a1:ident $i1:ident, $a2:ident $i2:ident) => {
impl_atomic!($a1 $i1);
impl_atomic!($a2 $i2);
};
($atomic:ident $i:ident) => {
impl HasAtomic for $i {
type Atomic = $atomic;
}

impl AtomicBits for $atomic {
type Bits = $i;
fn fetch_and(&self, val: Self::Bits, order: Ordering) -> Self::Bits {
self.fetch_and(val, order)
}
fn from_bits_retain(bits: <Self::Flags as $crate::Flags>::Bits) -> Self {
Self(Self::AtomicBits::from(bits))
fn fetch_or(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.fetch_or(val, order)
}
fn fetch_xor(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.fetch_xor(val, order)
}
fn load(&self, order: Ordering) -> Self::Bits{
self.load(order)
}
fn store(&self, val: Self::Bits, order: Ordering){
self.store(val, order)
}
fn swap(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.swap(val, order)
}
}
};
}

impl_atomic!(AtomicU8 u8, AtomicI8 i8);
impl_atomic!(AtomicU16 u16, AtomicI16 i16);
impl_atomic!(AtomicU32 u32, AtomicI32 i32);
impl_atomic!(AtomicU64 u64, AtomicI64 i64);
impl_atomic!(AtomicUsize usize, AtomicIsize isize);

/// Wrapper to enable atomic bitflag operations
///
///
#[repr(transparent)]
pub struct Atomic<F>(<<F as Flags>::Bits as HasAtomic>::Atomic)
where F: Flags, <F as Flags>::Bits: HasAtomic;

impl<F> From<F> for Atomic<F>
where F: Flags, <F as Flags>::Bits: HasAtomic
{
fn from(value: F) -> Self {
Self(value.bits().into())
}
}

impl<F> core::fmt::Debug for Atomic<F>
where
F: Flags + core::fmt::Debug,
<F as Flags>::Bits: HasAtomic
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let flags = self.load(Ordering::Relaxed);
core::fmt::Debug::fmt(&flags, f)
}
}

impl<F> Default for Atomic<F>
where
F: Flags + Default,
<F as Flags>::Bits: HasAtomic
{
fn default() -> Self {
F::default().into()
}
}

impl<F> Atomic<F>
where F: Flags, <F as Flags>::Bits: HasAtomic
{
pub fn empty() -> Self {
F::empty().into()
}

pub fn all() -> Self {
F::all().into()
}

pub fn load(&self, ordering: Ordering) -> F {
F::from_bits_retain(self.0.load(ordering))
}

pub fn store(&self, value: F, ordering: Ordering) {
self.0.store(value.bits(), ordering);
}

pub fn swap(&self, value: F, ordering: Ordering) -> F {
let bits = self.0.swap(value.bits(), ordering);
F::from_bits_retain(bits)
}

pub fn fetch_insert(&self, value: F, ordering: Ordering) -> F {
let bits = self.0.fetch_or(value.bits(), ordering);
F::from_bits_retain(bits)
}

pub fn fetch_remove(&self, value: F, ordering: Ordering) -> F {
let bits = self.0.fetch_and(!value.bits(), ordering);
F::from_bits_retain(bits)
}

pub fn fetch_toggle(&self, value: F, ordering: Ordering) -> F {
let bits = self.0.fetch_xor(value.bits(), ordering);
F::from_bits_retain(bits)
}

pub fn fetch_set(&self, val: F, set: bool, ordering: Ordering) -> F {
if set {
self.fetch_insert(val, ordering)
} else {
self.fetch_remove(val, ordering)
}
}
}
4 changes: 3 additions & 1 deletion src/lib.rs
Expand Up @@ -243,7 +243,9 @@ The result of `Flags::A ^ Flags::B` is `0b0000_0010`, which doesn't correspond t
#![cfg_attr(test, allow(mixed_script_confusables))]

#[doc(inline)]
pub use traits::{Bits, Flag, Flags, HasAtomic, AtomicBits, AtomicFlags};
pub use traits::{Bits, Flag, Flags};
#[doc(inline)]
pub use atomic::{Atomic, AtomicBits, HasAtomic};

pub mod iter;
pub mod parser;
Expand Down
1 change: 1 addition & 0 deletions src/tests.rs
@@ -1,4 +1,5 @@
mod all;
mod atomic;
mod bits;
mod complement;
mod contains;
Expand Down
54 changes: 54 additions & 0 deletions src/tests/atomic.rs
@@ -0,0 +1,54 @@
use core::{sync::atomic::Ordering, fmt::Debug};

use super::*;

use crate::{Atomic, Flags, HasAtomic};

#[test]
fn cases() {
case(
TestFlags::ABC,
&[
(&|a| a.load(Ordering::Relaxed),TestFlags::ABC),
],
TestFlags::ABC
);
case(
TestFlags::A,
&[
(&|a| a.swap(TestFlags::B, Ordering::Relaxed),TestFlags::A),
(&|a| a.swap(TestFlags::C, Ordering::Relaxed),TestFlags::B),
],
TestFlags::C
);
case(
TestFlags::empty(),
&[
(&|a| a.fetch_insert(TestFlags::A, Ordering::Relaxed), TestFlags::empty()),
(&|a| a.fetch_toggle(TestFlags::ABC, Ordering::Relaxed), TestFlags::A),
(&|a| a.fetch_remove(TestFlags::C, Ordering::Relaxed), TestFlags::B | TestFlags::C),
],
TestFlags::B
);
}

#[track_caller]
fn case<F: Flags>(
init: F,
ops: &[(&dyn Fn(&Atomic<F>) -> F, F)],
final_res: F
)
where
F: Flags + Debug + Copy + PartialEq,
<F as Flags>::Bits: HasAtomic,
{
let atomic = Atomic::from(init);

for (op, op_res) in ops {
let op_val = op(&atomic);
assert_eq!(&op_val, op_res, "expected={op_res:?} got={op_val:?}");
}

let final_val = atomic.load(Ordering::Relaxed);
assert_eq!(final_val, final_res, "expected={final_res:?} got={final_val:?}");
}
120 changes: 1 addition & 119 deletions src/traits.rs
@@ -1,7 +1,6 @@
use core::{
fmt,
ops::{BitAnd, BitOr, BitXor, Not},
sync::atomic::*,
ops::{BitAnd, BitOr, BitXor, Not}
};

use crate::{
Expand Down Expand Up @@ -314,57 +313,6 @@ pub trait Flags: Sized + 'static {
}
}

pub trait AtomicFlags : Sized + 'static {
type AtomicBits: AtomicBits<Bits=<Self::Flags as Flags>::Bits>;
type Flags: Flags;

fn atomic_bits(&self) -> &Self::AtomicBits;
fn from_bits_retain(bits: <Self::Flags as Flags>::Bits) -> Self;

fn empty() -> Self {
Self::from_bits_retain(<Self::Flags as Flags>::empty().bits())
}
fn all() -> Self {
Self::from_bits_retain(<Self::Flags as Flags>::all().bits())
}
fn from_bits(bits: <Self::Flags as Flags>::Bits) -> Self {
Self::from_bits_retain(bits)
}
fn from_bits_truncate(bits: <Self::Flags as Flags>::Bits) -> Self {
Self::from_bits_retain(<Self::Flags as Flags>::from_bits_truncate(bits).bits())
}

fn swap(&self, val: Self::Flags, ordering: Ordering) -> Self::Flags {
let bits = self.atomic_bits().swap(val.bits(), ordering);
Self::Flags::from_bits_retain(bits)
}
fn store(&self, val: Self::Flags, ordering: Ordering) {
self.atomic_bits().store(val.bits(), ordering);
}
fn load(&self, ordering: Ordering) -> Self::Flags {
let bits = self.atomic_bits().load(ordering);
Self::Flags::from_bits_retain(bits)
}
fn fetch_insert(&self, val: Self::Flags, ordering: Ordering) -> Self::Flags {
let bits = self.atomic_bits().fetch_or(val.bits(), ordering);
Self::Flags::from_bits_retain(bits)
}
fn fetch_remove(&self, val: Self::Flags, ordering: Ordering) -> Self::Flags {
let bits = self.atomic_bits().fetch_and(!val.bits(), ordering);
Self::Flags::from_bits_retain(bits)
}
fn fetch_toggle(&self, val: Self::Flags, ordering: Ordering) -> Self::Flags {
let bits = self.atomic_bits().fetch_xor(val.bits(), ordering);
Self::Flags::from_bits_retain(bits)
}
fn fetch_set(&self, val: Self::Flags, set: bool, ordering: Ordering) -> Self::Flags {
if set {
self.fetch_insert(val, ordering)
} else {
self.fetch_remove(val, ordering)
}
}
}

/**
A bits type that can be used as storage for a flags type.
Expand All @@ -387,27 +335,6 @@ pub trait Bits:
const ALL: Self;
}

/// Bits type that has an atomic variant
pub trait HasAtomic : Bits {
type Atomic: AtomicBits<Bits=Self>;
}

/// A type that can be used as atomic storage for a flags type
pub trait AtomicBits :
From<Self::Bits>
+ Sized
+ 'static
{
type Bits: HasAtomic<Atomic=Self>;

fn fetch_and(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn fetch_or(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn fetch_xor(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
fn load(&self, order: Ordering) -> Self::Bits;
fn store(&self, val: Self::Bits, order: Ordering);
fn swap(&self, val: Self::Bits, order: Ordering) -> Self::Bits;
}

// Not re-exported: prevent custom `Bits` impls being used in the `bitflags!` macro,
// or they may fail to compile based on crate features
pub trait Primitive {}
Expand Down Expand Up @@ -464,51 +391,6 @@ impl_bits! {
usize, isize,
}

macro_rules! impl_atomic {
($a1:ident $i1:ident, $a2:ident $i2:ident) => {
impl_atomic!($a1 $i1);
impl_atomic!($a2 $i2);
};
($atomic:ident $i:ident) => {
impl HasAtomic for $i {
type Atomic = $atomic;
}

impl AtomicBits for $atomic {
type Bits = $i;
fn fetch_and(&self, val: Self::Bits, order: Ordering) -> Self::Bits {
self.fetch_and(val, order)
}
fn fetch_or(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.fetch_or(val, order)
}
fn fetch_xor(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.fetch_xor(val, order)
}
fn load(&self, order: Ordering) -> Self::Bits{
self.load(order)
}
fn store(&self, val: Self::Bits, order: Ordering){
self.store(val, order)
}
fn swap(&self, val: Self::Bits, order: Ordering) -> Self::Bits{
self.swap(val, order)
}
}
};
}

#[cfg(target_has_atomic = "8")]
impl_atomic!(AtomicU8 u8, AtomicI8 i8);
#[cfg(target_has_atomic = "16")]
impl_atomic!(AtomicU16 u16, AtomicI16 i16);
#[cfg(target_has_atomic = "32")]
impl_atomic!(AtomicU32 u32, AtomicI32 i32);
#[cfg(target_has_atomic = "64")]
impl_atomic!(AtomicU64 u64, AtomicI64 i64);
#[cfg(target_has_atomic = "ptr")]
impl_atomic!(AtomicUsize usize, AtomicIsize isize);

/// A trait for referencing the `bitflags`-owned internal type
/// without exposing it publicly.
pub trait PublicFlags {
Expand Down

0 comments on commit 0efb392

Please sign in to comment.