#include <atomic>
#include <cassert>
#include <thread>
#include <type_traits>
#include <utility>
// Make a bitfield class that has a private BaseT bitfield.
// Then, for each bitfield member, make a method that returns a bitfield_member
// for that.
//
// Ex:
// class MyBitfield {
// unsigned long long bits;
// using A_type = bitfield_member<unsigned long long, bool, 0, 1>;
// using A_const = bitfield_member<unsigned long long, const bool, 0, 1>;
// using B_type = bitfield_member<unsigned long long, unsigned short, 1, 16>
// public:
// A_type A() {return {bits};}
// A_const A() const {return {bits};}
// B_type B() {return {bits};}
// B_const B() const {return {bits};}
// };
//
template<class BaseT, class FieldT, unsigned bitcount, unsigned offset>
class bitfield_member {
static_assert(bitcount>0);
static_assert(bitcount<sizeof(BaseT)*8);
static_assert(offset<sizeof(BaseT)*8-bitcount);
static const BaseT lomask = static_cast<BaseT>((1ull<<bitcount)-1ull);
BaseT* bits;
public:
constexpr explicit bitfield_member(BaseT& bits_) :bits(&bits_) {}
constexpr operator FieldT() const {return get();}
constexpr FieldT get() const {return static_cast<FieldT>((*bits>>offset)&lomask);}
constexpr void set(FieldT value) {
assert((value&mask)==value);
*bits = (*bits&~(lomask<<offset))|(static_cast<BaseT>(value)<<offset);
}
constexpr bitfield_member& operator=(FieldT value) {set(value); return *this;}
constexpr bitfield_member& operator++() {set(get()++); return *this;}
constexpr FieldT operator++(int) {FieldT t=get(); set(get()++); return t;}
constexpr bitfield_member& operator--() {set(get()--); return *this;}
constexpr FieldT operator--(int) {FieldT t=get(); set(get()--); return t;}
constexpr FieldT operator+() const {return get();}
constexpr FieldT operator-() const {return -get();}
constexpr FieldT operator!() const {return !get();}
constexpr FieldT operator~() const {return ~get();}
constexpr FieldT operator*(FieldT value) const {return get()*value;}
constexpr bitfield_member& operator*=(FieldT value) {set(get()*value); return *this;}
constexpr FieldT operator/(FieldT value) const {return get()/value;}
constexpr bitfield_member& operator/=(FieldT value) {set(get()/value); return *this;}
constexpr FieldT operator%(FieldT value) const {return get()%value;}
constexpr bitfield_member& operator%=(FieldT value) {set(get()%value); return *this;}
constexpr FieldT operator+(FieldT value) const {return get()+value;}
constexpr bitfield_member& operator+=(FieldT value) {set(get()+value); return *this;}
constexpr FieldT operator-(FieldT value) const {return get()-value;}
constexpr bitfield_member& operator-=(FieldT value) {set(get()-value); return *this;}
constexpr FieldT operator<<(FieldT value) const {return get()<<value;}
constexpr bitfield_member& operator<<=(FieldT value) {set(get()<<=value); return *this;}
constexpr FieldT operator>>(FieldT value) const {return get()>>value;}
constexpr bitfield_member& operator>>=(FieldT value) {set(get()>>=value); return *this;}
constexpr bool operator<(FieldT value) const {return get()<value;}
constexpr bool operator<=(FieldT value) const {return get()<=value;}
constexpr bool operator>(FieldT value) const {return get()>value;}
constexpr bool operator>=(FieldT value) const {return get()>=value;}
constexpr bool operator==(FieldT value) const {return get()==value;}
constexpr bool operator!=(FieldT value) const {return get()!=value;}
constexpr FieldT operator&(FieldT value) const {return get()&value;}
constexpr bitfield_member& operator&=(FieldT value) {set(get()&value); return *this;}
constexpr FieldT operator^(FieldT value) const {return get()^value;}
constexpr bitfield_member& operator^=(FieldT value) {set(get()^value); return *this;}
constexpr FieldT operator|(FieldT value) const {return get()|value;}
constexpr bitfield_member& operator|=(FieldT value) {set(get()|value); return *this;}
};
enum class atomic_bitfield_mutate_step {store, abort, yield_and_retry};
template<class T, class F>
std::pair<bool,T> atomic_bitfield_mutate(std::atomic<T>& atomic, F mutation) {
#if __cpp_lib_is_invocable
static_assert(std::is_invocable_r_v<atomic_bitfield_mutate_step, F, T&>);
#endif
T old_value = atomic.load(std::memory_order_consume);
T store_value;
do {
store_value = old_value;
auto step = mutation(store_value);
if (step == atomic_bitfield_mutate_step::abort) {return {false, old_value};}
if (step == atomic_bitfield_mutate_step::yield_and_retry) {std::this_thread::yield(); old_value = atomic.load(std::memory_order_consume); continue;}
} while(!atomic.compare_exchange_weak(old_value, store_value));
return {true, store_value};
}
template<class T, class F, class...Args>
T atomic_bitfield_busy_wait_until(std::atomic<T>& atomic, Args&&...params, F is_done_waiting) {
#if __cpp_lib_is_invocable
static_assert(std::is_invocable_r_v<bool, F, T&, Args...>);
#endif
T old_value;
bool is_done;
do {
old_value = atomic.load(std::memory_order_consume);
is_done = is_done_waiting(old_value, std::forward<Args>(params)...);
} while(!is_done && (std::this_thread::yield(),true));
return old_value;
}
//USAGE:
class SingleProducerConsumer
{
struct bitfield {
using read_idx_t = bitfield_member<unsigned, unsigned, 8, 0>;
using write_idx_t = bitfield_member<unsigned, unsigned, 8, 8>;
using end_idx_t = bitfield_member<unsigned, unsigned, 8, 16>;
using is_end_found_t = bitfield_member<unsigned, bool, 1, 24>;
using should_abort_t = bitfield_member<unsigned, bool, 1, 25>;
unsigned value;
read_idx_t read_idx() {return read_idx_t{value};}
write_idx_t write_idx() {return write_idx_t{value};}
end_idx_t end_idx() {return end_idx_t{value};}
is_end_found_t is_end_found() {return is_end_found_t{value};}
should_abort_t should_abort() {return should_abort_t{value};}
};
std::atomic<bitfield> async_flags;
int values[32];
void produce(int value) {
unsigned write_idx = async_flags.load(std::memory_order_consume).write_idx();
values[write_idx] = value;
unsigned next_idx = (write_idx+1)%32;
std::pair<bool,bitfield> did_advance = atomic_bitfield_mutate(async_flags, [=](bitfield& flags) {
if (flags.should_abort()) return atomic_bitfield_mutate_step::abort;
if (next_idx == flags.read_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
flags.write_idx() = next_idx;
return atomic_bitfield_mutate_step::store;
});
}
void producer_mark_end() {
unsigned idx = async_flags.load(std::memory_order_consume).write_idx();
unsigned next_idx = (idx + 1) % 32;
atomic_bitfield_mutate(async_flags, [=](bitfield& flags) {
if (flags.should_abort()) return atomic_bitfield_mutate_step::abort;
if (next_idx == flags.read_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
flags.write_idx() = next_idx;
flags.end_idx() = idx;
flags.is_end_found() = true;
return atomic_bitfield_mutate_step::store;
});
}
bool consumer_at_end() const {
bitfield flags = async_flags.load(std::memory_order_consume);
return flags.is_end_found() && flags.read_idx()==flags.end_idx();
}
int consumer_read() {
bitfield flags = atomic_bitfield_busy_wait_until(async_flags, [](bitfield& flags) {
return flags.read_idx() != flags.write_idx();
});
unsigned idx = flags.read_idx();
return values[idx];
}
void consumer_advance() {
std::pair<bool,bitfield> r = atomic_bitfield_mutate(async_flags, [](bitfield& flags) {
unsigned old_idx = flags.read_idx();
if (old_idx == flags.write_idx()) return atomic_bitfield_mutate_step::yield_and_retry;
flags.read_idx() = (old_idx+1) % 32;
return atomic_bitfield_mutate_step::store;
});
}
};
#include <iostream>
int main() {
SingleProducerConsumer t;
}