Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,29 @@ auto maybe = x + y; // std::expected<primitive<int, policy::error::expected>, po
- `policy::value::{checked, unchecked, saturating}`
- `policy::type::{strict, compatible, transparent}`
- `policy::error::{throwing, expected, terminate}`
- `policy::concurrency::{none, atomic}`
- `policy::concurrency::{none, fenced, fenced_relaxed, fenced_acq_rel, fenced_seq_cst}`

并发策略说明:

- `fenced*` 系列是操作级并发语义,通过策略注入内存序 fence;
- `primitive` 存储仍保持统一、零开销布局,不引入额外存储层抽象;
- `primitive::load/store/compare_exchange` 由并发策略的协议实现提供,若策略未实现该协议会在编译期报错。

示例(并发访问 API):

```cpp
using shared_t = primitive<int, policy::value::checked,
policy::concurrency::fenced_acq_rel,
policy::error::expected>;

shared_t v{1};
v.store(2);
auto expected = 2;
if (v.compare_exchange(expected, 3)) {
auto now = v.load();
(void)now;
}
```

默认策略位于 `policy::defaults`:

Expand Down
24 changes: 17 additions & 7 deletions examples/ex05_concurrency_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
* Example: ex05_concurrency_policy
*
* Purpose:
* Demonstrate the atomic concurrency policy path under multi-threaded
* Demonstrate the fenced concurrency policy path under multi-threaded
* repeated dispatch.
*
* Expected results:
* - Concurrent add operations consistently produce value 42.
* - Primitive load/store/CAS APIs work under fenced policy.
* - mismatch_count remains zero after all worker threads join.
* - Program prints a success message and exits with code 0.
*/
Expand All @@ -21,13 +22,22 @@ import mcpplibs.primitives;
using namespace mcpplibs::primitives;

int main() {
// Point 5: Use atomic concurrency policy and verify concurrent consistency.
using atomic_t =
primitive<int, policy::value::checked, policy::concurrency::atomic,
// Point 5: Use fenced concurrency policy and verify concurrent consistency.
using fenced_t =
primitive<int, policy::value::checked, policy::concurrency::fenced,
policy::error::expected>;

auto const lhs = atomic_t{12};
auto const rhs = atomic_t{30};
auto const lhs = fenced_t{12};
auto const rhs = fenced_t{30};

auto concurrent_value = fenced_t{1};
concurrent_value.store(2);
auto expected = 2;
if (!concurrent_value.compare_exchange(expected, 3) ||
concurrent_value.load() != 3) {
std::cerr << "fenced load/store/CAS mismatch\n";
return 1;
}

std::atomic<int> mismatch_count{0};
std::vector<std::thread> workers;
Expand All @@ -51,7 +61,7 @@ int main() {

// A non-zero mismatch count indicates unexpected behavior under concurrency.
if (mismatch_count.load(std::memory_order_relaxed) != 0) {
std::cerr << "atomic policy path mismatch\n";
std::cerr << "fenced policy path mismatch\n";
return 1;
}

Expand Down
4 changes: 4 additions & 0 deletions examples/ex07_custom_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
* - Program prints a success message and exits with code 0.
*/

#include <atomic>
#include <expected>
#include <iostream>
#include <type_traits>


import mcpplibs.primitives;
import mcpplibs.primitives.operations.invoker;

Expand Down Expand Up @@ -79,6 +81,8 @@ struct mcpplibs::primitives::policy::concurrency::handler<
injection_type out{};
out.fence_before = true;
out.fence_after = false;
out.order_before = std::memory_order_acquire;
out.order_after = std::memory_order_relaxed;
return out;
}
};
Expand Down
9 changes: 5 additions & 4 deletions src/operations/invoker.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,14 @@ constexpr auto make_div_zero(char const *reason)
return make_error<CommonRep>(policy::error::kind::divide_by_zero, reason);
}

constexpr auto apply_runtime_fence(bool enabled) noexcept -> void {
constexpr auto apply_runtime_fence(bool enabled,
std::memory_order order) noexcept -> void {
if (!enabled) {
return;
}

if (!std::is_constant_evaluated()) {
std::atomic_thread_fence(std::memory_order_seq_cst);
std::atomic_thread_fence(order);
}
}

Expand Down Expand Up @@ -660,12 +661,12 @@ constexpr auto run_value(CommonRep lhs, CommonRep rhs,
op_binding_available<OpTag, ValuePolicy, CommonRep>,
"Missing operation binding specialization for this OpTag/common type");

details::apply_runtime_fence(injection.fence_before);
details::apply_runtime_fence(injection.fence_before, injection.order_before);

auto decision = op_binding<OpTag, ValuePolicy, CommonRep>::apply(lhs, rhs);
auto finalized = ValueHandler::finalize(std::move(decision), injection);

details::apply_runtime_fence(injection.fence_after);
details::apply_runtime_fence(injection.fence_after, injection.order_after);
return finalized;
}

Expand Down
44 changes: 44 additions & 0 deletions src/policy/handler.cppm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module;

#include <atomic>
#include <concepts>
#include <expected>
#include <optional>
Expand Down Expand Up @@ -84,6 +85,8 @@ namespace concurrency {
struct injection {
bool fence_before = false;
bool fence_after = false;
std::memory_order order_before = std::memory_order_seq_cst;
std::memory_order order_after = std::memory_order_seq_cst;
};

template <typename Policy, typename OpTag, typename CommonRep,
Expand All @@ -94,6 +97,17 @@ struct handler {
using result_type = std::expected<CommonRep, ErrorPayload>;

static constexpr auto inject() noexcept -> injection_type { return {}; }

static constexpr auto load(CommonRep const &) noexcept -> CommonRep {
return CommonRep{};
}

static constexpr auto store(CommonRep &, CommonRep) noexcept -> void {}

static constexpr auto compare_exchange(CommonRep &, CommonRep &,
CommonRep) noexcept -> bool {
return false;
}
};

template <typename Policy, typename OpTag, typename CommonRep,
Expand Down Expand Up @@ -126,6 +140,36 @@ concept handler_available = requires {
requires handler_protocol<Policy, OpTag, CommonRep, ErrorPayload>;
};

template <typename Policy, typename CommonRep,
typename ErrorPayload = error::kind>
concept handler_access_protocol = requires {
requires concurrency_policy<Policy>;
{
handler<Policy, void, CommonRep, ErrorPayload>::enabled
} -> std::convertible_to<bool>;
requires handler<Policy, void, CommonRep, ErrorPayload>::enabled;
{
handler<Policy, void, CommonRep, ErrorPayload>::load(
std::declval<CommonRep const &>())
} noexcept -> std::same_as<CommonRep>;
{
handler<Policy, void, CommonRep, ErrorPayload>::store(
std::declval<CommonRep &>(), std::declval<CommonRep>())
} noexcept -> std::same_as<void>;
{
handler<Policy, void, CommonRep, ErrorPayload>::compare_exchange(
std::declval<CommonRep &>(), std::declval<CommonRep &>(),
std::declval<CommonRep>())
} noexcept -> std::same_as<bool>;
};

template <typename Policy, typename CommonRep,
typename ErrorPayload = error::kind>
concept handler_access_available = requires {
requires handler<Policy, void, CommonRep, ErrorPayload>::enabled;
requires handler_access_protocol<Policy, CommonRep, ErrorPayload>;
};

} // namespace concurrency

namespace type {
Expand Down
Loading
Loading