Skip to content

Commit

Permalink
Added proof of concept for atomic bitflags
Browse files Browse the repository at this point in the history
  • Loading branch information
f-gagel committed Jan 7, 2024
1 parent 586d819 commit 8b478ac
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/atomic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! 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.

#[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
}
fn from_bits_retain(bits: <Self::Flags as $crate::Flags>::Bits) -> Self {
Self(Self::AtomicBits::from(bits))
}
}
}
}
4 changes: 4 additions & 0 deletions src/example_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ __declare_public_bitflags! {
pub struct Flags
}

__declare_atomic_bitflags! {
pub struct AtomicFlags: Flags
}

__declare_internal_bitflags! {
pub struct Field0: u32
}
Expand Down
34 changes: 33 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ 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};
pub use traits::{Bits, Flag, Flags, HasAtomic, AtomicBits, AtomicFlags};

pub mod iter;
pub mod parser;
Expand Down Expand Up @@ -444,6 +444,36 @@ bitflags! {
*/
#[macro_export(local_inner_macros)]
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 @@ -911,6 +941,8 @@ macro_rules! __bitflags_flag {
#[macro_use]
mod public;
#[macro_use]
mod atomic;
#[macro_use]
mod internal;
#[macro_use]
mod external;
Expand Down
119 changes: 119 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::{
fmt,
ops::{BitAnd, BitOr, BitXor, Not},
sync::atomic::*,
};

use crate::{
Expand Down Expand Up @@ -313,6 +314,58 @@ 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 @@ -334,6 +387,27 @@ 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 @@ -390,6 +464,51 @@ 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
15 changes: 15 additions & 0 deletions tests/compile-pass/atomic_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use bitflags::{bitflags, AtomicFlags};

bitflags! {
#[atomic_attr(derive(Default))]
#[atomic(MyAtomicFlags)]
pub struct MyFlags: u32 {
const A = 1;
const B = 2;
}
}

fn main() {
let flags = MyAtomicFlags::default();
flags.fetch_insert(MyFlags::A, core::sync::atomic::Ordering::Relaxed);
}

0 comments on commit 8b478ac

Please sign in to comment.