Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added atomic bitflags generation #391

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
153 changes: 153 additions & 0 deletions src/atomic.rs
@@ -0,0 +1,153 @@
//! Generate the user-facing atomic flags type.
//!
//! The code here belongs to the end-user, so new trait implementations and methods can't be
//! added without potentially breaking users.

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 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)
}
}
}
32 changes: 32 additions & 0 deletions src/lib.rs
Expand Up @@ -444,6 +444,36 @@ bitflags! {
*/
#[macro_export]
macro_rules! bitflags {
(
$(#[atomic_attr($outer_atomic:meta)])*
#[atomic($Atomic:ident)]
$(#[$outer:meta])*
$vis:vis struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
const $Flag:tt = $value:expr;
)*
}

$($t:tt)*
)=> {
__declare_atomic_bitflags! {
$(#[$outer_atomic])*
$vis struct $Atomic : $BitFlags
}

bitflags! {
$(#[$outer])*
$vis struct $BitFlags: $T {
$(
$(#[$inner $($args)*])*
const $Flag = $value;
)*
}

$($t)*
}
};
(
$(#[$outer:meta])*
$vis:vis struct $BitFlags:ident: $T:ty {
Expand Down Expand Up @@ -908,6 +938,8 @@ macro_rules! __bitflags_flag {
};
}

pub mod atomic;

#[macro_use]
mod public;
#[macro_use]
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::{Flags, atomic::{Atomic, 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:?}");
}
3 changes: 2 additions & 1 deletion src/traits.rs
@@ -1,6 +1,6 @@
use core::{
fmt,
ops::{BitAnd, BitOr, BitXor, Not},
ops::{BitAnd, BitOr, BitXor, Not}
};

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


/**
A bits type that can be used as storage for a flags type.
*/
Expand Down