rename chapters
This commit is contained in:
196
Chapter17/cib/libs/conc/atomic.hpp
Normal file
196
Chapter17/cib/libs/conc/atomic.hpp
Normal 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
|
||||
56
Chapter17/cib/libs/conc/concepts.hpp
Normal file
56
Chapter17/cib/libs/conc/concepts.hpp
Normal 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
|
||||
67
Chapter17/cib/libs/conc/concurrency.hpp
Normal file
67
Chapter17/cib/libs/conc/concurrency.hpp
Normal 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
|
||||
Reference in New Issue
Block a user