add Chapter18

This commit is contained in:
Amar Mahmutbegovic
2025-02-06 00:19:59 +01:00
parent 9cc9cc7d73
commit 8634accda5
1533 changed files with 1092521 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
#pragma once
#include <conc/concepts.hpp>
#include <atomic>
#include <memory>
#include <type_traits>
#if __cplusplus >= 202002L
#define CPP20(...) __VA_ARGS__
#else
#define CPP20(...)
#endif
namespace atomic {
namespace detail {
struct standard_policy {
static_assert(static_cast<int>(std::memory_order_relaxed) ==
__ATOMIC_RELAXED);
static_assert(static_cast<int>(std::memory_order_acquire) ==
__ATOMIC_ACQUIRE);
static_assert(static_cast<int>(std::memory_order_release) ==
__ATOMIC_RELEASE);
static_assert(static_cast<int>(std::memory_order_seq_cst) ==
__ATOMIC_SEQ_CST);
static_assert(static_cast<int>(std::memory_order_acq_rel) ==
__ATOMIC_ACQ_REL);
static_assert(static_cast<int>(std::memory_order_consume) ==
__ATOMIC_CONSUME);
template <typename T>
static auto load(T const &t,
std::memory_order mo = std::memory_order_seq_cst) -> T {
T ret{};
__atomic_load(std::addressof(t), std::addressof(ret),
static_cast<int>(mo));
return ret;
}
template <typename T>
static auto
store(T &t, T &value,
std::memory_order mo = std::memory_order_seq_cst) -> void {
__atomic_store(std::addressof(t), std::addressof(value),
static_cast<int>(mo));
}
template <typename T>
static auto
exchange(T &t, T &value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
T ret{};
__atomic_exchange(std::addressof(t), std::addressof(value),
std::addressof(ret), static_cast<int>(mo));
return ret;
}
template <typename T>
static auto
fetch_add(T &t, T value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
return __atomic_fetch_add(std::addressof(t), value,
static_cast<int>(mo));
}
template <typename T>
static auto
fetch_sub(T &t, T value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
return __atomic_fetch_sub(std::addressof(t), value,
static_cast<int>(mo));
}
template <typename T>
static auto
fetch_and(T &t, T value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
return __atomic_fetch_and(std::addressof(t), value,
static_cast<int>(mo));
}
template <typename T>
static auto
fetch_or(T &t, T value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
return __atomic_fetch_or(std::addressof(t), value,
static_cast<int>(mo));
}
template <typename T>
static auto
fetch_xor(T &t, T value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
return __atomic_fetch_xor(std::addressof(t), value,
static_cast<int>(mo));
}
};
} // namespace detail
template <typename...> inline auto injected_policy = detail::standard_policy{};
template <typename... DummyArgs, typename T>
CPP20(requires(sizeof...(DummyArgs) == 0))
[[nodiscard]] auto load(T const &t,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(load_store_policy)
auto &p = injected_policy<DummyArgs...>;
return p.load(t, mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto store(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> void {
CPP20(load_store_policy)
auto &p = injected_policy<DummyArgs...>;
auto v = static_cast<T>(value);
p.store(t, v, mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
[[nodiscard]] auto exchange(
T &t, U value, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(exchange_policy)
auto &p = injected_policy<DummyArgs...>;
auto v = static_cast<T>(value);
return p.exchange(t, v, mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto fetch_add(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(add_sub_policy)
auto &p = injected_policy<DummyArgs...>;
return p.fetch_add(t, static_cast<T>(value), mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto fetch_sub(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(add_sub_policy)
auto &p = injected_policy<DummyArgs...>;
return p.fetch_sub(t, static_cast<T>(value), mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto fetch_and(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(bitwise_policy)
auto &p = injected_policy<DummyArgs...>;
return p.fetch_and(t, static_cast<T>(value), mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto fetch_or(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(bitwise_policy)
auto &p = injected_policy<DummyArgs...>;
return p.fetch_or(t, static_cast<T>(value), mo);
}
template <typename... DummyArgs, typename T, typename U,
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
CPP20(requires(sizeof...(DummyArgs) == 0))
auto fetch_xor(T &t, U value,
std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(bitwise_policy)
auto &p = injected_policy<DummyArgs...>;
return p.fetch_xor(t, static_cast<T>(value), mo);
}
template <typename T> struct atomic_type {
using type = T;
};
template <typename T> using atomic_type_t = typename atomic_type<T>::type;
template <typename T>
constexpr inline auto alignment_of = alignof(std::atomic<atomic_type_t<T>>);
} // namespace atomic
#undef CPP20
#ifdef ATOMIC_CFG
#include ATOMIC_CFG
#endif

View File

@@ -0,0 +1,56 @@
#pragma once
#if __cplusplus >= 202002L
#include <atomic>
#include <concepts>
namespace conc {
template <typename T>
concept policy = requires(auto (*f)()->int &&, auto (*pred)()->bool) {
{ T::call_in_critical_section(f) } -> std::same_as<int &&>;
{ T::call_in_critical_section(f, pred) } -> std::same_as<int &&>;
};
} // namespace conc
namespace atomic {
template <typename T>
concept load_store_policy = requires(int &a, int value, std::memory_order mo) {
{ T::load(a) } -> std::same_as<int>;
{ T::load(a, mo) } -> std::same_as<int>;
{ T::store(a, value) } -> std::same_as<void>;
{ T::store(a, value, mo) } -> std::same_as<void>;
};
template <typename T>
concept exchange_policy =
load_store_policy<T> and requires(int &a, int value, std::memory_order mo) {
{ T::exchange(a, value) } -> std::same_as<int>;
{ T::exchange(a, value, mo) } -> std::same_as<int>;
};
template <typename T>
concept add_sub_policy =
load_store_policy<T> and requires(int &a, int value, std::memory_order mo) {
{ T::fetch_add(a, value) } -> std::same_as<int>;
{ T::fetch_add(a, value, mo) } -> std::same_as<int>;
{ T::fetch_sub(a, value) } -> std::same_as<int>;
{ T::fetch_sub(a, value, mo) } -> std::same_as<int>;
};
template <typename T>
concept bitwise_policy =
load_store_policy<T> and requires(int &a, int value, std::memory_order mo) {
{ T::fetch_and(a, value) } -> std::same_as<int>;
{ T::fetch_and(a, value, mo) } -> std::same_as<int>;
{ T::fetch_or(a, value) } -> std::same_as<int>;
{ T::fetch_or(a, value, mo) } -> std::same_as<int>;
{ T::fetch_xor(a, value) } -> std::same_as<int>;
{ T::fetch_xor(a, value, mo) } -> std::same_as<int>;
};
template <typename T>
concept policy = exchange_policy<T> and add_sub_policy<T> and bitwise_policy<T>;
} // namespace atomic
#endif

View File

@@ -0,0 +1,67 @@
#pragma once
#ifdef SIMULATE_FREESTANDING
#define HAS_MUTEX 0
#else
#define HAS_MUTEX __has_include(<mutex>)
#endif
#include <conc/concepts.hpp>
#if HAS_MUTEX
#include <mutex>
#endif
#include <concepts>
#include <utility>
namespace conc {
namespace detail {
template <typename...> constexpr auto always_false_v = false;
#if HAS_MUTEX
template <typename Mutex = std::mutex> class standard_policy {
template <typename> static inline Mutex m{};
public:
template <typename Uniq = void, std::invocable F, std::predicate... Pred>
requires(sizeof...(Pred) < 2)
static auto call_in_critical_section(F &&f, Pred &&...pred)
-> decltype(std::forward<F>(f)()) {
while (true) {
[[maybe_unused]] std::lock_guard l{m<Uniq>};
if ((... and pred())) {
return std::forward<F>(f)();
}
}
}
};
#else
template <typename = void> struct standard_policy {
template <typename = void, std::invocable F, std::predicate... Pred>
requires(sizeof...(Pred) < 2)
static auto call_in_critical_section(F &&f, Pred &&...)
-> decltype(std::forward<F>(f)()) {
static_assert(always_false_v<F, Pred...>,
"No standard concurrency policy defined: inject a policy "
"by specializing conc::injected_policy<>");
return std::forward<F>(f)();
}
};
#endif
} // namespace detail
template <typename...> inline auto injected_policy = detail::standard_policy{};
template <typename Uniq = decltype([] {}), typename... DummyArgs,
std::invocable F, std::predicate... Pred>
requires(sizeof...(DummyArgs) == 0 and sizeof...(Pred) < 2)
auto call_in_critical_section(F &&f, Pred &&...pred)
-> decltype(std::forward<F>(f)()) {
policy auto &p = injected_policy<DummyArgs...>;
return p.template call_in_critical_section<Uniq>(
std::forward<F>(f), std::forward<Pred>(pred)...);
}
} // namespace conc
#undef HAS_MUTEX