From 8135abc985557b65ef86890b3653a1bbe3cde798 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 15:52:13 +0800 Subject: [PATCH 01/13] docs: Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 00504a5..9b29fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .xmake build/ build-*/ +out/ # xlings .xlings @@ -50,4 +51,4 @@ Makefile # IDE /.idea -/.cache \ No newline at end of file +/.cache From 74e70d211221274779c70fcfaa4da146b5848186 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 15:53:18 +0800 Subject: [PATCH 02/13] refactor: Impl concurrency::handler for concurrency::none --- src/policy/impl.cppm | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index 0ec7c17..bc94e7e 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -239,6 +239,33 @@ struct concurrency::handler { } }; +template +struct concurrency::handler { + static constexpr bool enabled = true; + using injection_type = concurrency::injection; + using result_type = std::expected; + + static constexpr auto load(CommonRep const &value) noexcept -> CommonRep { + return value; + } + + static constexpr auto store(CommonRep &value, CommonRep desired) noexcept + -> void { + value = desired; + } + + static constexpr auto compare_exchange(CommonRep &value, CommonRep &expected, + CommonRep desired) noexcept -> bool { + if (value != expected) { + expected = value; + return false; + } + + value = desired; + return true; + } +}; + template struct concurrency::handler Date: Sat, 21 Mar 2026 15:54:06 +0800 Subject: [PATCH 03/13] refactor: Use load to ensure concurrency correction --- src/operations/dispatcher.cppm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operations/dispatcher.cppm b/src/operations/dispatcher.cppm index 64a5e7d..cf2c973 100644 --- a/src/operations/dispatcher.cppm +++ b/src/operations/dispatcher.cppm @@ -123,9 +123,9 @@ constexpr auto dispatch(Lhs const &lhs, Rhs const &rhs) // Runtime stage 2: value path. auto const lhs_rep_raw = - underlying::traits::to_rep(lhs.value()); + underlying::traits::to_rep(lhs.load()); auto const rhs_rep_raw = - underlying::traits::to_rep(rhs.value()); + underlying::traits::to_rep(rhs.load()); if (!underlying::traits::is_valid_rep( lhs_rep_raw) || From 286a44f408d451257fc71752491ef1400ba26c18 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 15:55:06 +0800 Subject: [PATCH 04/13] feat: Add copy constructor/assignment operator and move constructor/assignment operator for primitive --- src/primitive/impl.cppm | 53 +++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index 45ae186..c631f66 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -58,6 +58,26 @@ public: "Multiple concurrency policies are not allowed"); constexpr explicit primitive(value_type v) noexcept : value_(v) {} + primitive(primitive const &other) noexcept : value_(other.load()) {} + auto operator=(primitive const &other) noexcept -> primitive & { + if (this == &other) { + return *this; + } + + store(other.load()); + return *this; + } + + primitive(primitive &&other) noexcept : value_(other.load()) {} + auto operator=(primitive &&other) noexcept -> primitive & { + if (this == &other) { + return *this; + } + + store(other.load()); + return *this; + } + constexpr value_type &value() noexcept { return value_; } [[nodiscard]] constexpr value_type const &value() const noexcept { return value_; @@ -65,43 +85,34 @@ public: constexpr explicit operator value_type() const noexcept { return value_; } [[nodiscard]] auto load() const noexcept -> value_type { - using access_handler_t = - policy::concurrency::handler; - static_assert( - policy::concurrency::handler_access_available, - "Selected concurrency policy does not provide primitive " - "load/store/CAS support"); + require_access_handler_(); return access_handler_t::load(value_); } auto store(value_type desired) noexcept -> void { - using access_handler_t = - policy::concurrency::handler; - static_assert( - policy::concurrency::handler_access_available, - "Selected concurrency policy does not provide primitive " - "load/store/CAS support"); + require_access_handler_(); access_handler_t::store(value_, desired); } auto compare_exchange(value_type &expected, value_type desired) noexcept -> bool { - using access_handler_t = - policy::concurrency::handler; + require_access_handler_(); + return access_handler_t::compare_exchange(value_, expected, desired); + } + +private: + using access_handler_t = + policy::concurrency::handler; + + static constexpr auto require_access_handler_() noexcept -> void { static_assert( policy::concurrency::handler_access_available, "Selected concurrency policy does not provide primitive " "load/store/CAS support"); - return access_handler_t::compare_exchange(value_, expected, desired); } -private: value_type value_; }; From 124c3af9fea1a94ff317f883493d804882fb6245 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 15:56:02 +0800 Subject: [PATCH 05/13] test: Update test expected result for concurrency::none --- tests/basic/test_policies.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 7872382..77a2191 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -122,6 +122,9 @@ TEST(PolicyConcurrencyTest, FencedVariantsUseExpectedMemoryOrders) { } TEST(PolicyConcurrencyTest, PrimitiveAccessHandlerProtocolByPolicy) { + EXPECT_TRUE( + (policy::concurrency::handler_access_available)); EXPECT_TRUE(( policy::concurrency::handler_access_available)); @@ -131,9 +134,6 @@ TEST(PolicyConcurrencyTest, PrimitiveAccessHandlerProtocolByPolicy) { policy::concurrency::fenced_acq_rel, int>)); EXPECT_TRUE((policy::concurrency::handler_access_available< policy::concurrency::fenced_seq_cst, int>)); - EXPECT_FALSE( - (policy::concurrency::handler_access_available)); } TEST(PolicyConcurrencyTest, PrimitiveAccessRejectsNonTriviallyCopyableRep) { From 7f71a2960b7c0f9893e26fb3d7588d3ba8727e55 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 15:56:21 +0800 Subject: [PATCH 06/13] test: Add more concurrency tests --- tests/basic/test_operations.cpp | 117 ++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/tests/basic/test_operations.cpp b/tests/basic/test_operations.cpp index ceb1fcc..b894775 100644 --- a/tests/basic/test_operations.cpp +++ b/tests/basic/test_operations.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include import mcpplibs.primitives; @@ -196,6 +197,122 @@ TEST(OperationsTest, PrimitiveFencedCasSupportsConcurrentIncrements) { EXPECT_EQ(counter.load(), kThreadCount * kIterationsPerThread); } +TEST(OperationsTest, + BinaryOperationsWithLoadStoreRemainStableUnderHighConcurrency) { + using value_t = + primitive; + + constexpr int kWriterThreads = 6; + constexpr int kReaderThreads = 8; + constexpr int kIterationsPerThread = 25000; + constexpr int kMaxOperand = 100000; + + auto lhs = value_t{0}; + auto rhs = value_t{0}; + auto sink = value_t{0}; + + std::atomic add_error_count{0}; + std::atomic sub_error_count{0}; + std::atomic range_violation_count{0}; + std::atomic start{false}; + + std::vector workers; + workers.reserve(kWriterThreads + kReaderThreads); + + for (int writer = 0; writer < kWriterThreads; ++writer) { + workers.emplace_back([&, writer]() { + while (!start.load(std::memory_order_acquire)) { + } + + for (int n = 0; n < kIterationsPerThread; ++n) { + auto const v1 = (writer + n) % (kMaxOperand + 1); + auto const v2 = (writer * 3 + n * 7) % (kMaxOperand + 1); + lhs.store(v1); + rhs.store(v2); + } + }); + } + + for (int reader = 0; reader < kReaderThreads; ++reader) { + workers.emplace_back([&, reader]() { + while (!start.load(std::memory_order_acquire)) { + } + + for (int n = 0; n < kIterationsPerThread; ++n) { + if (((reader + n) & 1) == 0) { + auto const out = operations::add(lhs, rhs); + if (!out.has_value()) { + add_error_count.fetch_add(1, std::memory_order_relaxed); + continue; + } + + auto const v = out->load(); + if (v < 0 || v > (kMaxOperand * 2)) { + range_violation_count.fetch_add(1, std::memory_order_relaxed); + } + sink.store(v); + auto const snapshot = sink.load(); + if (snapshot < -kMaxOperand || snapshot > (kMaxOperand * 2)) { + range_violation_count.fetch_add(1, std::memory_order_relaxed); + } + continue; + } + + auto const out = operations::sub(lhs, rhs); + if (!out.has_value()) { + sub_error_count.fetch_add(1, std::memory_order_relaxed); + continue; + } + + auto const v = out->load(); + if (v < -kMaxOperand || v > kMaxOperand) { + range_violation_count.fetch_add(1, std::memory_order_relaxed); + } + sink.store(v); + auto const snapshot = sink.load(); + if (snapshot < -kMaxOperand || snapshot > (kMaxOperand * 2)) { + range_violation_count.fetch_add(1, std::memory_order_relaxed); + } + } + }); + } + + start.store(true, std::memory_order_release); + + for (auto &worker : workers) { + worker.join(); + } + + EXPECT_EQ(add_error_count.load(std::memory_order_relaxed), 0); + EXPECT_EQ(sub_error_count.load(std::memory_order_relaxed), 0); + EXPECT_EQ(range_violation_count.load(std::memory_order_relaxed), 0); +} + +TEST(OperationsTest, PrimitiveSupportsCopyAndMoveSpecialMembers) { + using value_t = primitive; + + static_assert(std::is_copy_constructible_v); + static_assert(std::is_copy_assignable_v); + static_assert(std::is_move_constructible_v); + static_assert(std::is_move_assignable_v); + + auto original = value_t{42}; + auto copy_constructed = value_t{original}; + EXPECT_EQ(copy_constructed.load(), 42); + + auto copy_assigned = value_t{0}; + copy_assigned = original; + EXPECT_EQ(copy_assigned.load(), 42); + + auto move_constructed = value_t{std::move(copy_assigned)}; + EXPECT_EQ(move_constructed.load(), 42); + + auto move_assigned = value_t{0}; + move_assigned = std::move(move_constructed); + EXPECT_EQ(move_assigned.load(), 42); +} + TEST(OperationsTest, StrictTypeRejectsMixedTypesAtCompileTime) { using lhs_t = primitive; From 99ed668855d8419f44564f79495233227bf25988 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 16:29:08 +0800 Subject: [PATCH 07/13] fix: Add impl concurrency::handler of demo::custom_concurrency to fix compile error --- examples/ex07_custom_policy.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/examples/ex07_custom_policy.cpp b/examples/ex07_custom_policy.cpp index ef36c13..f0bd159 100644 --- a/examples/ex07_custom_policy.cpp +++ b/examples/ex07_custom_policy.cpp @@ -87,6 +87,34 @@ struct mcpplibs::primitives::policy::concurrency::handler< } }; +template +struct mcpplibs::primitives::policy::concurrency::handler< + demo::custom_concurrency, void, CommonRep, ErrorPayload> { + static constexpr bool enabled = true; + using injection_type = mcpplibs::primitives::policy::concurrency::injection; + using result_type = std::expected; + + static constexpr auto load(CommonRep const &value) noexcept -> CommonRep { + return value; + } + + static constexpr auto store(CommonRep &value, CommonRep desired) noexcept + -> void { + value = desired; + } + + static constexpr auto compare_exchange(CommonRep &value, CommonRep &expected, + CommonRep desired) noexcept -> bool { + if (value != expected) { + expected = value; + return false; + } + + value = desired; + return true; + } +}; + // Point 7 / Step 3C: Implement custom value handler. // Complex point: finalize() post-processes decision and adjusts output. template Date: Sat, 21 Mar 2026 16:41:39 +0800 Subject: [PATCH 08/13] fix: Trying to fix ci failure --- .github/workflows/ci.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e97953e..3642f18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,22 +62,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Install Xlings - env: - XLINGS_NON_INTERACTIVE: 1 - run: | - curl -fsSL https://raw.githubusercontent.com/d2learn/xlings/refs/heads/main/tools/other/quick_install.sh | bash -s -- $XLINGS_VERSION - echo "PATH=$HOME/.xlings/subos/current/bin:$PATH" >> "$GITHUB_ENV" + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: latest + package-cache: true - - name: Install Project Dependencies via Xlings + - name: Verify toolchain run: | - xlings install + xmake --version clang --version - name: Build with xmake run: | xmake f -m release --toolchain=llvm -vv -y - xmake -j$(nproc) + xmake -j$(sysctl -n hw.ncpu) build-windows: runs-on: windows-latest From 990170b69e98c9e569a4a0606fde206e8b94432b Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 16:47:28 +0800 Subject: [PATCH 09/13] fix: Trying to fix ci failure #2 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3642f18..665b647 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: - name: Build with xmake run: | - xmake f -m release --toolchain=llvm -vv -y + xmake f -m release -vv -y xmake -j$(sysctl -n hw.ncpu) build-windows: From 99ae1dc6b6bab622ef2a7e54c0a9dcc52e5a6ce1 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 16:53:09 +0800 Subject: [PATCH 10/13] fix: Trying to fix ci failure #3 --- .github/workflows/ci.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 665b647..f66711b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: build-macos: runs-on: macos-15 - timeout-minutes: 20 + timeout-minutes: 25 steps: - name: Checkout uses: actions/checkout@v4 @@ -68,15 +68,20 @@ jobs: xmake-version: latest package-cache: true + - name: Install LLVM 20 + run: brew install llvm@20 + - name: Verify toolchain run: | xmake --version + /opt/homebrew/opt/llvm@20/bin/clang++ --version clang --version - name: Build with xmake run: | - xmake f -m release -vv -y - xmake -j$(sysctl -n hw.ncpu) + export PATH=/opt/homebrew/opt/llvm@20/bin:$PATH + xmake f -p macosx -m release --toolchain=llvm --sdk=/opt/homebrew/opt/llvm@20 -y -vv + xmake -y -vv -j$(sysctl -n hw.ncpu) build-windows: runs-on: windows-latest From c701e238f52b3712caa0bc90259a85035d385bf2 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 17:44:09 +0800 Subject: [PATCH 11/13] fix: Add consteval-friendly path --- src/primitive/impl.cppm | 68 ++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/primitive/impl.cppm b/src/primitive/impl.cppm index c631f66..f72a847 100644 --- a/src/primitive/impl.cppm +++ b/src/primitive/impl.cppm @@ -43,8 +43,7 @@ struct resolve_concurrency_policy { }; template -using resolve_concurrency_policy_t = - typename resolve_concurrency_policy::type; +using resolve_concurrency_policy_t = resolve_concurrency_policy::type; } // namespace details @@ -58,44 +57,85 @@ public: "Multiple concurrency policies are not allowed"); constexpr explicit primitive(value_type v) noexcept : value_(v) {} - primitive(primitive const &other) noexcept : value_(other.load()) {} - auto operator=(primitive const &other) noexcept -> primitive & { + + constexpr primitive(primitive const &other) noexcept { + if consteval { + value_ = other.value_; + } else { + value_ = other.load(); + } + } + + constexpr auto operator=(primitive const &other) noexcept -> primitive & { if (this == &other) { return *this; } - store(other.load()); + if consteval { + value_ = other.value_; + } else { + store(other.load()); + } return *this; } - primitive(primitive &&other) noexcept : value_(other.load()) {} - auto operator=(primitive &&other) noexcept -> primitive & { + constexpr primitive(primitive &&other) noexcept { + if consteval { + value_ = other.value_; + } else { + value_ = other.load(); + } + } + + constexpr auto operator=(primitive &&other) noexcept -> primitive & { if (this == &other) { return *this; } - store(other.load()); + if consteval { + value_ = other.value_; + } else { + store(other.load()); + } return *this; } constexpr value_type &value() noexcept { return value_; } + [[nodiscard]] constexpr value_type const &value() const noexcept { return value_; } + constexpr explicit operator value_type() const noexcept { return value_; } - [[nodiscard]] auto load() const noexcept -> value_type { + [[nodiscard]] constexpr auto load() const noexcept -> value_type { + if consteval { + return value_; + } require_access_handler_(); return access_handler_t::load(value_); } - auto store(value_type desired) noexcept -> void { - require_access_handler_(); - access_handler_t::store(value_, desired); + constexpr auto store(value_type desired) noexcept -> void { + if consteval { + value_ = desired; + } else { + require_access_handler_(); + access_handler_t::store(value_, desired); + } } - auto compare_exchange(value_type &expected, value_type desired) noexcept - -> bool { + constexpr auto compare_exchange(value_type &expected, + value_type desired) noexcept -> bool { + if consteval { + if (value_ != expected) { + expected = value_; + return false; + } + + value_ = desired; + return true; + } require_access_handler_(); return access_handler_t::compare_exchange(value_, expected, desired); } From f3f0e163c93a07f1d90a3ebbcb50f1235caaaea7 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 17:59:04 +0800 Subject: [PATCH 12/13] fix: Add constraint clauses and optimize error prompts --- src/policy/impl.cppm | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/policy/impl.cppm b/src/policy/impl.cppm index bc94e7e..cb173da 100644 --- a/src/policy/impl.cppm +++ b/src/policy/impl.cppm @@ -158,6 +158,20 @@ template constexpr void assert_atomic_ref_compatible() { "satisfy std::atomic_ref::required_alignment"); } +template +concept has_underlying_rep_not_equal = + underlying_type && + requires(T const &lhs, T const &rhs) { + { + underlying::traits>::to_rep(lhs) != + underlying::traits>::to_rep(rhs) + } -> std::convertible_to; + }; + +template +inline constexpr bool none_compare_exchange_available_v = + has_underlying_rep_not_equal; + template inline constexpr bool is_arithmetic_operation_v = operations::op_has_capability_v; @@ -255,8 +269,10 @@ struct concurrency::handler { } static constexpr auto compare_exchange(CommonRep &value, CommonRep &expected, - CommonRep desired) noexcept -> bool { - if (value != expected) { + CommonRep desired) noexcept -> bool + requires(details::none_compare_exchange_available_v) { + using traits_type = underlying::traits>; + if (traits_type::to_rep(value) != traits_type::to_rep(expected)) { expected = value; return false; } From b0fa0fdf54c9b149ddd9d8fe49ea8e710952f319 Mon Sep 17 00:00:00 2001 From: FrozenlemonTee <1115306170@qq.com> Date: Sat, 21 Mar 2026 17:59:30 +0800 Subject: [PATCH 13/13] test: Add test case --- tests/basic/test_policies.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/basic/test_policies.cpp b/tests/basic/test_policies.cpp index 77a2191..36b0e66 100644 --- a/tests/basic/test_policies.cpp +++ b/tests/basic/test_policies.cpp @@ -149,6 +149,9 @@ TEST(PolicyConcurrencyTest, PrimitiveAccessRejectsNonTriviallyCopyableRep) { } TEST(PolicyConcurrencyTest, PrimitiveAccessRespectsAtomicRefAlignmentGate) { + EXPECT_FALSE((policy::concurrency::handler_access_available< + policy::concurrency::none, LowAlignmentRep>)); + constexpr bool requires_stronger_alignment = std::atomic_ref::required_alignment > alignof(LowAlignmentRep);