From 3efea1ba0b47588adba7d9b147836c3825e39e84 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Tue, 15 Oct 2024 12:07:52 -0600 Subject: [PATCH] :art: Use atomic API from concurrency lib --- CMakeLists.txt | 4 ++- include/stdx/atomic_bitset.hpp | 58 ++++++++++++++++++--------------- test/CMakeLists.txt | 5 +++ test/atomic_bitset.cpp | 12 ++++--- test/atomic_bitset_override.cpp | 20 ++++++++++++ test/detail/atomic_cfg.hpp | 7 ++++ 6 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 test/atomic_bitset_override.cpp create mode 100644 test/detail/atomic_cfg.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1714ddb..3a9108b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ endif() add_versioned_package("gh:boostorg/mp11#boost-1.83.0") fmt_recipe(10.2.1) +add_versioned_package("gh:intel/cpp-baremetal-concurrency#27de8e1") if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 20) @@ -25,7 +26,8 @@ add_library(stdx INTERFACE) target_compile_features(stdx INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) target_compile_options( stdx INTERFACE $<$:-Wno-missing-braces>) -target_link_libraries_system(stdx INTERFACE boost_mp11 fmt::fmt-header-only) +target_link_libraries_system(stdx INTERFACE concurrency boost_mp11 + fmt::fmt-header-only) target_sources( stdx diff --git a/include/stdx/atomic_bitset.hpp b/include/stdx/atomic_bitset.hpp index 325d6b6..b4280b8 100644 --- a/include/stdx/atomic_bitset.hpp +++ b/include/stdx/atomic_bitset.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -22,7 +24,10 @@ template ())> class atomic_bitset { constexpr static std::size_t N = to_underlying(Size); - using elem_t = StorageElem; + + using elem_t = atomic::atomic_type_t; + constexpr static auto alignment = atomic::alignment_of; + static_assert(std::is_unsigned_v, "Storage element for atomic_bitset must be an unsigned type"); @@ -30,11 +35,11 @@ class atomic_bitset { static_assert(N <= std::numeric_limits::digits, "atomic_bitset is limited to a single storage element"); - std::atomic storage{}; + alignas(alignment) elem_t storage{}; constexpr static auto mask = bit_mask(); - elem_t salient_value(std::memory_order order) const { - return storage.load(order) & mask; + auto salient_value(std::memory_order order) const -> elem_t { + return atomic::load(storage, order) & mask; } [[nodiscard]] static constexpr auto value_from_string(std::string_view str, @@ -88,34 +93,32 @@ class atomic_bitset { "Conversion must be to an unsigned integral type or enum!"); static_assert(N <= std::numeric_limits::digits, "atomic_bitset must fit within T"); - return static_cast(storage.load(order)); + return static_cast(salient_value(order)); } [[nodiscard]] auto - to_natural(std::memory_order order = std::memory_order_seq_cst) const { - return storage.load(order); + to_natural(std::memory_order order = std::memory_order_seq_cst) const + -> StorageElem { + return static_cast(salient_value(order)); } - operator bitset_t() const { return bitset_t{storage.load()}; } + operator bitset_t() const { + return bitset_t{salient_value(std::memory_order_seq_cst)}; + } auto load(std::memory_order order = std::memory_order_seq_cst) const -> bitset_t { - return bitset_t{storage.load(order)}; + return bitset_t{salient_value(order)}; } auto store(bitset_t b, std::memory_order order = std::memory_order_seq_cst) { - storage.store(b.template to(), order); + atomic::store(storage, b.template to(), order); } constexpr static std::integral_constant size{}; - constexpr static std::bool_constant< - std::atomic::is_always_lock_free> - is_always_lock_free{}; - template [[nodiscard]] auto operator[](T idx) const -> bool { - auto const pos = static_cast(to_underlying(idx)); - return (salient_value(std::memory_order_seq_cst) & (bit << pos)) != 0; + return load()[idx]; } template @@ -123,11 +126,11 @@ class atomic_bitset { std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); if (value) { - return bitset_t{ - storage.fetch_or(static_cast(bit << pos), order)}; + return bitset_t{atomic::fetch_or( + storage, static_cast(bit << pos), order)}; } - return bitset_t{ - storage.fetch_and(static_cast(~(bit << pos)), order)}; + return bitset_t{atomic::fetch_and( + storage, static_cast(~(bit << pos)), order)}; } auto set(lsb_t lsb, msb_t msb, bool value = true, @@ -136,9 +139,9 @@ class atomic_bitset { auto const m = to_underlying(msb); auto const shifted_value = bit_mask(m, l); if (value) { - return bitset_t{storage.fetch_or(shifted_value, order)}; + return bitset_t{atomic::fetch_or(storage, shifted_value, order)}; } - return bitset_t{storage.fetch_and(~shifted_value, order)}; + return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)}; } auto set(lsb_t lsb, length_t len, bool value = true, @@ -150,18 +153,19 @@ class atomic_bitset { auto set(std::memory_order order = std::memory_order_seq_cst) LIFETIMEBOUND -> atomic_bitset & { - storage.store(mask, order); + atomic::store(storage, mask, order); return *this; } template auto reset(T idx) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); - return bitset_t{storage.fetch_and(static_cast(~(bit << pos)))}; + return bitset_t{ + atomic::fetch_and(storage, static_cast(~(bit << pos)))}; } auto reset(std::memory_order order = std::memory_order_seq_cst) LIFETIMEBOUND -> atomic_bitset & { - storage.store(elem_t{}, order); + atomic::store(storage, elem_t{}, order); return *this; } @@ -182,11 +186,11 @@ class atomic_bitset { std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); return bitset_t{ - storage.fetch_xor(static_cast(bit << pos), order)}; + atomic::fetch_xor(storage, static_cast(bit << pos), order)}; } auto flip(std::memory_order order = std::memory_order_seq_cst) -> bitset_t { - return bitset_t{storage.fetch_xor(mask, order)}; + return bitset_t{atomic::fetch_xor(storage, mask, order)}; } [[nodiscard]] auto diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e06a9d1..53951b6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ add_tests( algorithm always_false atomic_bitset + atomic_bitset_override bind bit bitset @@ -60,6 +61,10 @@ add_tests( utility udls) +target_compile_definitions( + atomic_bitset_override_test + PRIVATE -DATOMIC_CFG="${CMAKE_CURRENT_LIST_DIR}/detail/atomic_cfg.hpp") + if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20) add_tests(FILES ct_format ct_string indexed_tuple tuple tuple_algorithms) endif() diff --git a/test/atomic_bitset.cpp b/test/atomic_bitset.cpp index 52bfa29..4dfad11 100644 --- a/test/atomic_bitset.cpp +++ b/test/atomic_bitset.cpp @@ -39,11 +39,6 @@ TEST_CASE("atomic_bitset with implicit storage element type", static_assert(sizeof(stdx::atomic_bitset<64>) == sizeof(std::uint64_t)); } -TEMPLATE_TEST_CASE("atomic_bitset is always lock free", "[atomic_bitset]", - std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) { - static_assert(stdx::atomic_bitset<8, TestType>::is_always_lock_free); -} - TEMPLATE_TEST_CASE("index operation", "[atomic_bitset]", std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) { CHECK(not stdx::atomic_bitset<1, TestType>{}[0]); @@ -163,6 +158,13 @@ TEMPLATE_TEST_CASE("convert to natural type", "[atomic_bitset]", std::uint8_t, CHECK(bs.to_natural(std::memory_order_acquire) == 0b1000'1000u); } +TEST_CASE("to_natural returns smallest_uint", "[atomic_bitset]") { + auto bs = stdx::atomic_bitset<4>{stdx::all_bits}; + auto value = bs.to_natural(); + CHECK(value == 0b1111); + static_assert(std::same_as); +} + TEMPLATE_TEST_CASE("construct with a string_view", "[atomic_bitset]", std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) { using namespace std::string_view_literals; diff --git a/test/atomic_bitset_override.cpp b/test/atomic_bitset_override.cpp new file mode 100644 index 0000000..7f8c901 --- /dev/null +++ b/test/atomic_bitset_override.cpp @@ -0,0 +1,20 @@ +#include + +#include + +#include +#include + +TEST_CASE("atomic_bitset works with overridden type", + "[atomic_bitset_override]") { + auto bs = stdx::atomic_bitset<4>{}; + static_assert(sizeof(decltype(bs)) == sizeof(std::uint32_t)); + static_assert(alignof(decltype(bs)) == alignof(std::uint32_t)); +} + +TEST_CASE("to_natural returns smallest_uint", "[atomic_bitset_override]") { + auto bs = stdx::atomic_bitset<4>{stdx::all_bits}; + auto value = bs.to_natural(); + CHECK(value == 0b1111); + static_assert(std::same_as); +} diff --git a/test/detail/atomic_cfg.hpp b/test/detail/atomic_cfg.hpp new file mode 100644 index 0000000..b27f53c --- /dev/null +++ b/test/detail/atomic_cfg.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +template <> +struct atomic::atomic_type : std::type_identity {};