Skip to content

Commit 99231b9

Browse files
authored
Merge pull request #158 from elbeno/atomic-bitset-policy
🎨 Use atomic API from concurrency lib
2 parents 59a6cb1 + 3efea1b commit 99231b9

File tree

6 files changed

+73
-33
lines changed

6 files changed

+73
-33
lines changed

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ endif()
1616

1717
add_versioned_package("gh:boostorg/mp11#boost-1.83.0")
1818
fmt_recipe(10.2.1)
19+
add_versioned_package("gh:intel/cpp-baremetal-concurrency#27de8e1")
1920

2021
if(NOT DEFINED CMAKE_CXX_STANDARD)
2122
set(CMAKE_CXX_STANDARD 20)
@@ -25,7 +26,8 @@ add_library(stdx INTERFACE)
2526
target_compile_features(stdx INTERFACE cxx_std_${CMAKE_CXX_STANDARD})
2627
target_compile_options(
2728
stdx INTERFACE $<$<CXX_COMPILER_ID:Clang>:-Wno-missing-braces>)
28-
target_link_libraries_system(stdx INTERFACE boost_mp11 fmt::fmt-header-only)
29+
target_link_libraries_system(stdx INTERFACE concurrency boost_mp11
30+
fmt::fmt-header-only)
2931

3032
target_sources(
3133
stdx

include/stdx/atomic_bitset.hpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <conc/atomic.hpp>
4+
35
#include <stdx/bit.hpp>
46
#include <stdx/bitset.hpp>
57
#include <stdx/compiler.hpp>
@@ -22,19 +24,22 @@ template <auto Size,
2224
typename StorageElem = decltype(smallest_uint<to_underlying(Size)>())>
2325
class atomic_bitset {
2426
constexpr static std::size_t N = to_underlying(Size);
25-
using elem_t = StorageElem;
27+
28+
using elem_t = atomic::atomic_type_t<StorageElem>;
29+
constexpr static auto alignment = atomic::alignment_of<StorageElem>;
30+
2631
static_assert(std::is_unsigned_v<elem_t>,
2732
"Storage element for atomic_bitset must be an unsigned type");
2833

2934
constexpr static auto bit = elem_t{1U};
3035

3136
static_assert(N <= std::numeric_limits<elem_t>::digits,
3237
"atomic_bitset is limited to a single storage element");
33-
std::atomic<elem_t> storage{};
38+
alignas(alignment) elem_t storage{};
3439

3540
constexpr static auto mask = bit_mask<elem_t, N - 1>();
36-
elem_t salient_value(std::memory_order order) const {
37-
return storage.load(order) & mask;
41+
auto salient_value(std::memory_order order) const -> elem_t {
42+
return atomic::load(storage, order) & mask;
3843
}
3944

4045
[[nodiscard]] static constexpr auto value_from_string(std::string_view str,
@@ -88,46 +93,44 @@ class atomic_bitset {
8893
"Conversion must be to an unsigned integral type or enum!");
8994
static_assert(N <= std::numeric_limits<U>::digits,
9095
"atomic_bitset must fit within T");
91-
return static_cast<T>(storage.load(order));
96+
return static_cast<T>(salient_value(order));
9297
}
9398

9499
[[nodiscard]] auto
95-
to_natural(std::memory_order order = std::memory_order_seq_cst) const {
96-
return storage.load(order);
100+
to_natural(std::memory_order order = std::memory_order_seq_cst) const
101+
-> StorageElem {
102+
return static_cast<StorageElem>(salient_value(order));
97103
}
98104

99-
operator bitset_t() const { return bitset_t{storage.load()}; }
105+
operator bitset_t() const {
106+
return bitset_t{salient_value(std::memory_order_seq_cst)};
107+
}
100108

101109
auto load(std::memory_order order = std::memory_order_seq_cst) const
102110
-> bitset_t {
103-
return bitset_t{storage.load(order)};
111+
return bitset_t{salient_value(order)};
104112
}
105113
auto store(bitset_t b,
106114
std::memory_order order = std::memory_order_seq_cst) {
107-
storage.store(b.template to<elem_t>(), order);
115+
atomic::store(storage, b.template to<elem_t>(), order);
108116
}
109117

110118
constexpr static std::integral_constant<std::size_t, N> size{};
111119

112-
constexpr static std::bool_constant<
113-
std::atomic<elem_t>::is_always_lock_free>
114-
is_always_lock_free{};
115-
116120
template <typename T> [[nodiscard]] auto operator[](T idx) const -> bool {
117-
auto const pos = static_cast<std::size_t>(to_underlying(idx));
118-
return (salient_value(std::memory_order_seq_cst) & (bit << pos)) != 0;
121+
return load()[idx];
119122
}
120123

121124
template <typename T>
122125
auto set(T idx, bool value = true,
123126
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
124127
auto const pos = static_cast<std::size_t>(to_underlying(idx));
125128
if (value) {
126-
return bitset_t{
127-
storage.fetch_or(static_cast<elem_t>(bit << pos), order)};
129+
return bitset_t{atomic::fetch_or(
130+
storage, static_cast<elem_t>(bit << pos), order)};
128131
}
129-
return bitset_t{
130-
storage.fetch_and(static_cast<elem_t>(~(bit << pos)), order)};
132+
return bitset_t{atomic::fetch_and(
133+
storage, static_cast<elem_t>(~(bit << pos)), order)};
131134
}
132135

133136
auto set(lsb_t lsb, msb_t msb, bool value = true,
@@ -136,9 +139,9 @@ class atomic_bitset {
136139
auto const m = to_underlying(msb);
137140
auto const shifted_value = bit_mask<elem_t>(m, l);
138141
if (value) {
139-
return bitset_t{storage.fetch_or(shifted_value, order)};
142+
return bitset_t{atomic::fetch_or(storage, shifted_value, order)};
140143
}
141-
return bitset_t{storage.fetch_and(~shifted_value, order)};
144+
return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)};
142145
}
143146

144147
auto set(lsb_t lsb, length_t len, bool value = true,
@@ -150,18 +153,19 @@ class atomic_bitset {
150153

151154
auto set(std::memory_order order = std::memory_order_seq_cst)
152155
LIFETIMEBOUND -> atomic_bitset & {
153-
storage.store(mask, order);
156+
atomic::store(storage, mask, order);
154157
return *this;
155158
}
156159

157160
template <typename T> auto reset(T idx) -> bitset_t {
158161
auto const pos = static_cast<std::size_t>(to_underlying(idx));
159-
return bitset_t{storage.fetch_and(static_cast<elem_t>(~(bit << pos)))};
162+
return bitset_t{
163+
atomic::fetch_and(storage, static_cast<elem_t>(~(bit << pos)))};
160164
}
161165

162166
auto reset(std::memory_order order = std::memory_order_seq_cst)
163167
LIFETIMEBOUND -> atomic_bitset & {
164-
storage.store(elem_t{}, order);
168+
atomic::store(storage, elem_t{}, order);
165169
return *this;
166170
}
167171

@@ -182,11 +186,11 @@ class atomic_bitset {
182186
std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
183187
auto const pos = static_cast<std::size_t>(to_underlying(idx));
184188
return bitset_t{
185-
storage.fetch_xor(static_cast<elem_t>(bit << pos), order)};
189+
atomic::fetch_xor(storage, static_cast<elem_t>(bit << pos), order)};
186190
}
187191

188192
auto flip(std::memory_order order = std::memory_order_seq_cst) -> bitset_t {
189-
return bitset_t{storage.fetch_xor(mask, order)};
193+
return bitset_t{atomic::fetch_xor(storage, mask, order)};
190194
}
191195

192196
[[nodiscard]] auto

test/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_tests(
2222
algorithm
2323
always_false
2424
atomic_bitset
25+
atomic_bitset_override
2526
bind
2627
bit
2728
bitset
@@ -60,6 +61,10 @@ add_tests(
6061
utility
6162
udls)
6263

64+
target_compile_definitions(
65+
atomic_bitset_override_test
66+
PRIVATE -DATOMIC_CFG="${CMAKE_CURRENT_LIST_DIR}/detail/atomic_cfg.hpp")
67+
6368
if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)
6469
add_tests(FILES ct_format ct_string indexed_tuple tuple tuple_algorithms)
6570
endif()

test/atomic_bitset.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ TEST_CASE("atomic_bitset with implicit storage element type",
3939
static_assert(sizeof(stdx::atomic_bitset<64>) == sizeof(std::uint64_t));
4040
}
4141

42-
TEMPLATE_TEST_CASE("atomic_bitset is always lock free", "[atomic_bitset]",
43-
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) {
44-
static_assert(stdx::atomic_bitset<8, TestType>::is_always_lock_free);
45-
}
46-
4742
TEMPLATE_TEST_CASE("index operation", "[atomic_bitset]", std::uint8_t,
4843
std::uint16_t, std::uint32_t, std::uint64_t) {
4944
CHECK(not stdx::atomic_bitset<1, TestType>{}[0]);
@@ -163,6 +158,13 @@ TEMPLATE_TEST_CASE("convert to natural type", "[atomic_bitset]", std::uint8_t,
163158
CHECK(bs.to_natural(std::memory_order_acquire) == 0b1000'1000u);
164159
}
165160

161+
TEST_CASE("to_natural returns smallest_uint", "[atomic_bitset]") {
162+
auto bs = stdx::atomic_bitset<4>{stdx::all_bits};
163+
auto value = bs.to_natural();
164+
CHECK(value == 0b1111);
165+
static_assert(std::same_as<decltype(value), std::uint8_t>);
166+
}
167+
166168
TEMPLATE_TEST_CASE("construct with a string_view", "[atomic_bitset]",
167169
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) {
168170
using namespace std::string_view_literals;

test/atomic_bitset_override.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdx/atomic_bitset.hpp>
2+
3+
#include <catch2/catch_test_macros.hpp>
4+
5+
#include <concepts>
6+
#include <cstdint>
7+
8+
TEST_CASE("atomic_bitset works with overridden type",
9+
"[atomic_bitset_override]") {
10+
auto bs = stdx::atomic_bitset<4>{};
11+
static_assert(sizeof(decltype(bs)) == sizeof(std::uint32_t));
12+
static_assert(alignof(decltype(bs)) == alignof(std::uint32_t));
13+
}
14+
15+
TEST_CASE("to_natural returns smallest_uint", "[atomic_bitset_override]") {
16+
auto bs = stdx::atomic_bitset<4>{stdx::all_bits};
17+
auto value = bs.to_natural();
18+
CHECK(value == 0b1111);
19+
static_assert(std::same_as<decltype(value), std::uint8_t>);
20+
}

test/detail/atomic_cfg.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <type_traits>
5+
6+
template <>
7+
struct atomic::atomic_type<std::uint8_t> : std::type_identity<std::uint32_t> {};

0 commit comments

Comments
 (0)