455 lines
19 KiB
C++
455 lines
19 KiB
C++
#pragma once
|
|
#include "adminive/descriptor.hpp"
|
|
#include <concepts>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <ranges>
|
|
#include <shared_mutex>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
namespace adminive {
|
|
struct Empty_Lock {
|
|
void lock() noexcept {}
|
|
bool try_lock() noexcept {
|
|
return true;
|
|
}
|
|
void unlock() noexcept {}
|
|
void lock_shared() noexcept {}
|
|
bool try_lock_shared() noexcept {
|
|
return true;
|
|
}
|
|
void unlock_shared() noexcept {}
|
|
};
|
|
template <class T>
|
|
concept Basic_Lock = std::default_initializable<T> && requires(T& value) {
|
|
value.lock();
|
|
value.unlock();
|
|
};
|
|
template <class T>
|
|
concept Shared_Basic_Lock = Basic_Lock<T> && requires(T& value) {
|
|
value.lock_shared();
|
|
value.unlock_shared();
|
|
};
|
|
template <class T>
|
|
struct Is_Reference_Wrapper : std::false_type {};
|
|
template <class T>
|
|
struct Is_Reference_Wrapper<std::reference_wrapper<T>> : std::true_type {};
|
|
template <class Result>
|
|
concept Synchronized_Result = std::is_void_v<Result> || (!std::is_reference_v<Result> && !std::is_pointer_v<Result> && !Is_Reference_Wrapper<std::remove_cvref_t<Result>>::value && !std::ranges::view<std::remove_cvref_t<Result>>);
|
|
template <Basic_Lock Lock>
|
|
using Read_Lock_Guard = std::conditional_t<Shared_Basic_Lock<Lock>, std::shared_lock<Lock>, std::unique_lock<Lock>>;
|
|
template <class T>
|
|
using Descriptor_Type = decltype(Type_Descriptor<std::remove_cvref_t<T>>::get());
|
|
template <class T>
|
|
using Descriptor_Fields = std::remove_cvref_t<decltype(std::declval<Descriptor_Type<T>>().fields())>;
|
|
namespace detail {
|
|
template <std::size_t Index>
|
|
struct Reflected_Field_Path_Key {
|
|
static constexpr std::size_t index = Index;
|
|
};
|
|
template <class T>
|
|
struct Is_Reflected_Field_Path_Key : std::false_type {};
|
|
template <std::size_t Index>
|
|
struct Is_Reflected_Field_Path_Key<Reflected_Field_Path_Key<Index>> : std::true_type {};
|
|
template <class T>
|
|
inline constexpr bool is_reflected_field_path_key_v = Is_Reflected_Field_Path_Key<std::remove_cvref_t<T>>::value;
|
|
}
|
|
template <class T, auto Key, std::size_t Index = 0>
|
|
consteval std::size_t described_field_index() {
|
|
using Object = std::remove_cvref_t<T>;
|
|
using Fields = Descriptor_Fields<Object>;
|
|
if constexpr(Index == std::tuple_size_v<Fields>) {
|
|
return Index;
|
|
} else {
|
|
using Field = std::tuple_element_t<Index, Fields>;
|
|
if constexpr(std::is_member_object_pointer_v<decltype(Key)>) {
|
|
if constexpr(requires { Field::member; } && std::same_as<std::remove_cv_t<decltype(Field::member)>, decltype(Key)>) {
|
|
if constexpr(Field::member == Key) {
|
|
return Index;
|
|
}
|
|
}
|
|
} else if constexpr(detail::is_reflected_field_path_key_v<decltype(Key)>) {
|
|
if constexpr(requires { Field::index; } && std::same_as<typename Field::owner_type, Object>) {
|
|
if constexpr(Field::index == std::remove_cvref_t<decltype(Key)>::index) {
|
|
return Index;
|
|
}
|
|
}
|
|
}
|
|
return described_field_index<Object, Key, Index + 1>();
|
|
}
|
|
}
|
|
template <class T, auto Member, std::size_t Index = 0>
|
|
consteval std::size_t described_member_index() {
|
|
return described_field_index<T, Member, Index>();
|
|
}
|
|
template <class T>
|
|
inline constexpr std::size_t described_field_count_v = std::tuple_size_v<Descriptor_Fields<std::remove_cvref_t<T>>>;
|
|
template <class T, auto Key>
|
|
concept Described_Field_Key = Described_Type<std::remove_cvref_t<T>> && (described_field_index<T, Key>() < described_field_count_v<T>);
|
|
template <class T, auto Member>
|
|
concept Described_Member = std::is_member_object_pointer_v<decltype(Member)> && Described_Field_Key<T, Member>;
|
|
template <class T, std::size_t Index>
|
|
concept Described_Reflected_Field = Described_Field_Key<T, detail::Reflected_Field_Path_Key<Index>{}>;
|
|
template <class T>
|
|
bool described_type_requires_lock();
|
|
template <class Field>
|
|
bool descriptor_field_requires_lock(const Field& field) {
|
|
if(field.lock_mode() == Field_Lock_Mode::enabled) {
|
|
return true;
|
|
}
|
|
using Member = typename std::remove_cvref_t<Field>::member_type;
|
|
if(field.lock_mode() == Field_Lock_Mode::disabled) {
|
|
if constexpr(Described_Type<Member>) {
|
|
return described_type_requires_lock<Member>();
|
|
}
|
|
return false;
|
|
}
|
|
if(field.is_editable()) {
|
|
return true;
|
|
}
|
|
if constexpr(Described_Type<Member>) {
|
|
return described_type_requires_lock<Member>();
|
|
}
|
|
return false;
|
|
}
|
|
template <class T>
|
|
bool described_type_requires_lock() {
|
|
if constexpr(!Described_Type<std::remove_cvref_t<T>>) {
|
|
return false;
|
|
} else {
|
|
static const bool result = [] {
|
|
const auto descriptor = describe<std::remove_cvref_t<T>>();
|
|
bool required{};
|
|
std::apply([&](const auto&... field) {
|
|
required = (descriptor_field_requires_lock(field) || ...);
|
|
}, descriptor.fields());
|
|
return required;
|
|
}();
|
|
return result;
|
|
}
|
|
}
|
|
template <class T, auto Key>
|
|
requires Described_Field_Key<T, Key>
|
|
bool described_field_requires_lock() {
|
|
static const bool result = [] {
|
|
const auto descriptor = describe<std::remove_cvref_t<T>>();
|
|
constexpr std::size_t index = described_field_index<T, Key>();
|
|
return descriptor_field_requires_lock(std::get<index>(descriptor.fields()));
|
|
}();
|
|
return result;
|
|
}
|
|
template <class T, auto Member>
|
|
requires Described_Member<T, Member>
|
|
bool described_member_requires_lock() {
|
|
return described_field_requires_lock<T, Member>();
|
|
}
|
|
template <auto Key, class Object>
|
|
decltype(auto) synchronized_field_value(Object& object) noexcept {
|
|
if constexpr(std::is_member_object_pointer_v<decltype(Key)>) {
|
|
return object.*Key;
|
|
} else {
|
|
static_assert(detail::is_reflected_field_path_key_v<decltype(Key)>);
|
|
using Owner = std::remove_cvref_t<Object>;
|
|
return Reflection_Adapter<Owner>::template get<std::remove_cvref_t<decltype(Key)>::index>(object);
|
|
}
|
|
}
|
|
template <class T, auto Key, auto... Rest>
|
|
bool described_path_requires_lock() {
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
return described_field_requires_lock<T, Key>();
|
|
} else {
|
|
using Next = std::remove_cvref_t<decltype(synchronized_field_value<Key>(std::declval<T&>()))>;
|
|
return described_path_requires_lock<Next, Rest...>();
|
|
}
|
|
}
|
|
template <class Field, Basic_Lock Lock>
|
|
struct Field_Lock_Node;
|
|
template <class T, Basic_Lock Lock, bool Enabled = Described_Type<std::remove_cvref_t<T>>>
|
|
class Field_Lock_Tree {};
|
|
template <class T, Basic_Lock Lock>
|
|
class Field_Lock_Tree<T, Lock, true> {
|
|
private:
|
|
using Fields = Descriptor_Fields<std::remove_cvref_t<T>>;
|
|
template <std::size_t... Indexes>
|
|
static auto make_nodes(std::index_sequence<Indexes...>) -> std::tuple<Field_Lock_Node<std::tuple_element_t<Indexes, Fields>, Lock>...>;
|
|
public:
|
|
using Nodes = decltype(make_nodes(std::make_index_sequence<std::tuple_size_v<Fields>>{}));
|
|
template <std::size_t Index>
|
|
auto& node() noexcept {
|
|
return std::get<Index>(nodes_);
|
|
}
|
|
template <std::size_t Index>
|
|
const auto& node() const noexcept {
|
|
return std::get<Index>(nodes_);
|
|
}
|
|
private:
|
|
Nodes nodes_{};
|
|
};
|
|
template <class Field, Basic_Lock Lock>
|
|
struct Field_Lock_Node {
|
|
using value_type = typename std::remove_cvref_t<Field>::member_type;
|
|
mutable Lock lock{};
|
|
Field_Lock_Tree<value_type, Lock> children{};
|
|
};
|
|
namespace detail {
|
|
struct Synchronized_Access;
|
|
}
|
|
template <class Root, Basic_Lock Object_Lock, Basic_Lock Field_Lock, auto... Members>
|
|
class Synchronized_Field;
|
|
template <class T, Basic_Lock Lock = std::shared_mutex, Basic_Lock Field_Lock = Lock>
|
|
class Synchronized_Value {
|
|
public:
|
|
using value_type = T;
|
|
using lock_type = Lock;
|
|
using field_lock_type = Field_Lock;
|
|
Synchronized_Value() requires std::default_initializable<T> = default;
|
|
explicit Synchronized_Value(T value) : value_(std::move(value)) {}
|
|
Synchronized_Value(const Synchronized_Value&) = delete;
|
|
Synchronized_Value& operator=(const Synchronized_Value&) = delete;
|
|
template <class Function>
|
|
requires Synchronized_Result<std::invoke_result_t<Function, const T&>>
|
|
decltype(auto) read(Function&& function) const {
|
|
std::unique_lock guard(lock_);
|
|
return std::invoke(std::forward<Function>(function), std::as_const(value_));
|
|
}
|
|
template <class Function>
|
|
requires Synchronized_Result<std::invoke_result_t<Function, T&>>
|
|
decltype(auto) write(Function&& function) {
|
|
std::unique_lock guard(lock_);
|
|
return std::invoke(std::forward<Function>(function), value_);
|
|
}
|
|
T snapshot() const requires std::copy_constructible<T> {
|
|
return read([](const T& value) {
|
|
return value;
|
|
});
|
|
}
|
|
void replace(T value) requires std::assignable_from<T&, T> {
|
|
write([&](T& target) {
|
|
target = std::move(value);
|
|
});
|
|
}
|
|
template <auto Member>
|
|
requires Described_Member<T, Member>
|
|
auto member() noexcept {
|
|
return Synchronized_Field<T, Lock, Field_Lock, Member>(*this);
|
|
}
|
|
template <auto Member>
|
|
requires Described_Member<T, Member>
|
|
auto member() const noexcept {
|
|
return Synchronized_Field<const T, Lock, Field_Lock, Member>(*this);
|
|
}
|
|
template <std::size_t Index>
|
|
requires Described_Reflected_Field<T, Index>
|
|
auto field() noexcept {
|
|
return Synchronized_Field<T, Lock, Field_Lock, detail::Reflected_Field_Path_Key<Index>{}>(*this);
|
|
}
|
|
template <std::size_t Index>
|
|
requires Described_Reflected_Field<T, Index>
|
|
auto field() const noexcept {
|
|
return Synchronized_Field<const T, Lock, Field_Lock, detail::Reflected_Field_Path_Key<Index>{}>(*this);
|
|
}
|
|
private:
|
|
friend struct detail::Synchronized_Access;
|
|
T& unsafe_value() noexcept {
|
|
return value_;
|
|
}
|
|
const T& unsafe_value() const noexcept {
|
|
return value_;
|
|
}
|
|
Lock& mutex() const noexcept {
|
|
return lock_;
|
|
}
|
|
template <class Root, Basic_Lock Object_Lock, Basic_Lock Member_Lock, auto... Members>
|
|
friend class Synchronized_Field;
|
|
T value_{};
|
|
mutable Lock lock_{};
|
|
mutable Field_Lock_Tree<T, Field_Lock> field_locks_{};
|
|
};
|
|
template <auto Key, class Object>
|
|
decltype(auto) synchronized_path_value(Object& object) noexcept {
|
|
return synchronized_field_value<Key>(object);
|
|
}
|
|
template <auto Key, auto Next, auto... Rest, class Object>
|
|
decltype(auto) synchronized_path_value(Object& object) noexcept {
|
|
return synchronized_path_value<Next, Rest...>(synchronized_field_value<Key>(object));
|
|
}
|
|
template <class Current, std::size_t Depth, auto Key, auto... Rest>
|
|
struct Synchronized_Path_Owner {
|
|
using Next = std::remove_cvref_t<decltype(synchronized_field_value<Key>(std::declval<Current&>()))>;
|
|
using type = typename Synchronized_Path_Owner<Next, Depth - 1, Rest...>::type;
|
|
};
|
|
template <class Current, auto Key, auto... Rest>
|
|
struct Synchronized_Path_Owner<Current, 0, Key, Rest...> {
|
|
using type = Current;
|
|
};
|
|
template <class Root, Basic_Lock Object_Lock, Basic_Lock Field_Lock, auto... Members>
|
|
class Synchronized_Field {
|
|
static_assert(sizeof...(Members) > 0);
|
|
public:
|
|
using root_type = std::remove_const_t<Root>;
|
|
using value_type = std::remove_cvref_t<decltype(synchronized_path_value<Members...>(std::declval<Root&>()))>;
|
|
using lock_type = Object_Lock;
|
|
using field_lock_type = Field_Lock;
|
|
using owner_type = std::conditional_t<std::is_const_v<Root>, const Synchronized_Value<root_type, Object_Lock, Field_Lock>, Synchronized_Value<root_type, Object_Lock, Field_Lock>>;
|
|
explicit Synchronized_Field(Synchronized_Value<root_type, Object_Lock, Field_Lock>& owner) requires (!std::is_const_v<Root>) : owner_(&owner) {}
|
|
explicit Synchronized_Field(const Synchronized_Value<root_type, Object_Lock, Field_Lock>& owner) requires std::is_const_v<Root> : owner_(&owner) {}
|
|
template <class Function>
|
|
requires Synchronized_Result<std::invoke_result_t<Function, const value_type&>>
|
|
decltype(auto) read(Function&& function) const {
|
|
return with_read_lock([&] {
|
|
return std::invoke(std::forward<Function>(function), std::as_const(unsafe_value()));
|
|
});
|
|
}
|
|
template <class Function>
|
|
requires (!std::is_const_v<Root>) && Synchronized_Result<std::invoke_result_t<Function, value_type&>>
|
|
decltype(auto) write(Function&& function) {
|
|
return with_write_lock([&] {
|
|
return std::invoke(std::forward<Function>(function), unsafe_value());
|
|
});
|
|
}
|
|
template <class Function>
|
|
requires Synchronized_Result<std::invoke_result_t<Function, const value_type&>>
|
|
decltype(auto) object_read(Function&& function) const {
|
|
return read(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
requires (!std::is_const_v<Root>) && Synchronized_Result<std::invoke_result_t<Function, value_type&>>
|
|
decltype(auto) object_write(Function&& function) {
|
|
return write(std::forward<Function>(function));
|
|
}
|
|
value_type snapshot() const requires std::copy_constructible<value_type> {
|
|
return object_read([](const value_type& value) {
|
|
return value;
|
|
});
|
|
}
|
|
void replace(value_type value) requires (!std::is_const_v<Root>) && std::assignable_from<value_type&, value_type> {
|
|
object_write([&](value_type& target) {
|
|
target = std::move(value);
|
|
});
|
|
}
|
|
template <auto Member>
|
|
requires Described_Member<value_type, Member>
|
|
auto member() noexcept requires (!std::is_const_v<Root>) {
|
|
return Synchronized_Field<root_type, Object_Lock, Field_Lock, Members..., Member>(*owner_);
|
|
}
|
|
template <auto Member>
|
|
requires Described_Member<value_type, Member>
|
|
auto member() const noexcept {
|
|
return Synchronized_Field<const root_type, Object_Lock, Field_Lock, Members..., Member>(*owner_);
|
|
}
|
|
template <std::size_t Index>
|
|
requires Described_Reflected_Field<value_type, Index>
|
|
auto field() noexcept requires (!std::is_const_v<Root>) {
|
|
return Synchronized_Field<root_type, Object_Lock, Field_Lock, Members..., detail::Reflected_Field_Path_Key<Index>{}>(*owner_);
|
|
}
|
|
template <std::size_t Index>
|
|
requires Described_Reflected_Field<value_type, Index>
|
|
auto field() const noexcept {
|
|
return Synchronized_Field<const root_type, Object_Lock, Field_Lock, Members..., detail::Reflected_Field_Path_Key<Index>{}>(*owner_);
|
|
}
|
|
private:
|
|
friend struct detail::Synchronized_Access;
|
|
decltype(auto) unsafe_value() const noexcept {
|
|
if constexpr(std::is_const_v<Root>) {
|
|
return std::as_const(synchronized_path_value<Members...>(owner_->unsafe_value()));
|
|
} else {
|
|
return synchronized_path_value<Members...>(owner_->unsafe_value());
|
|
}
|
|
}
|
|
Object_Lock& mutex() const noexcept {
|
|
return owner_->mutex();
|
|
}
|
|
template <class Function>
|
|
decltype(auto) with_read_lock(Function&& function) const {
|
|
Read_Lock_Guard<Object_Lock> root_guard(owner_->mutex());
|
|
if(!described_path_requires_lock<root_type, Members...>()) {
|
|
return std::forward<Function>(function)();
|
|
}
|
|
return with_path_read_lock<0, Members...>(owner_->field_locks_, std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
decltype(auto) with_write_lock(Function&& function) const {
|
|
if(!described_path_requires_lock<root_type, Members...>()) {
|
|
std::unique_lock root_guard(owner_->mutex());
|
|
return std::forward<Function>(function)();
|
|
}
|
|
Read_Lock_Guard<Object_Lock> root_guard(owner_->mutex());
|
|
return with_path_write_lock<0, Members...>(owner_->field_locks_, std::forward<Function>(function));
|
|
}
|
|
template <std::size_t Depth, auto Member, auto... Rest, class Tree, class Function>
|
|
static decltype(auto) with_path_read_lock(Tree& tree, Function&& function) {
|
|
using Object = typename Synchronized_Path_Owner<root_type, Depth, Members...>::type;
|
|
constexpr std::size_t index = described_field_index<Object, Member>();
|
|
auto& node = tree.template node<index>();
|
|
const bool lock_required = described_field_requires_lock<Object, Member>();
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
if(!lock_required) {
|
|
return std::forward<Function>(function)();
|
|
}
|
|
if constexpr(Described_Type<typename std::remove_cvref_t<decltype(node)>::value_type>) {
|
|
if(described_type_requires_lock<typename std::remove_cvref_t<decltype(node)>::value_type>()) {
|
|
std::unique_lock guard(node.lock);
|
|
return std::forward<Function>(function)();
|
|
}
|
|
}
|
|
Read_Lock_Guard<Field_Lock> guard(node.lock);
|
|
return std::forward<Function>(function)();
|
|
} else {
|
|
if(lock_required) {
|
|
Read_Lock_Guard<Field_Lock> guard(node.lock);
|
|
return with_path_read_lock<Depth + 1, Rest...>(node.children, std::forward<Function>(function));
|
|
}
|
|
return with_path_read_lock<Depth + 1, Rest...>(node.children, std::forward<Function>(function));
|
|
}
|
|
}
|
|
template <std::size_t Depth, auto Member, auto... Rest, class Tree, class Function>
|
|
static decltype(auto) with_path_write_lock(Tree& tree, Function&& function) {
|
|
using Object = typename Synchronized_Path_Owner<root_type, Depth, Members...>::type;
|
|
constexpr std::size_t index = described_field_index<Object, Member>();
|
|
auto& node = tree.template node<index>();
|
|
const bool lock_required = described_field_requires_lock<Object, Member>();
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
if(lock_required) {
|
|
std::unique_lock guard(node.lock);
|
|
return std::forward<Function>(function)();
|
|
}
|
|
return std::forward<Function>(function)();
|
|
} else {
|
|
if(lock_required) {
|
|
Read_Lock_Guard<Field_Lock> guard(node.lock);
|
|
return with_path_write_lock<Depth + 1, Rest...>(node.children, std::forward<Function>(function));
|
|
}
|
|
return with_path_write_lock<Depth + 1, Rest...>(node.children, std::forward<Function>(function));
|
|
}
|
|
}
|
|
owner_type* owner_;
|
|
};
|
|
namespace detail {
|
|
struct Synchronized_Access {
|
|
template <class T, Basic_Lock Lock, Basic_Lock Field_Lock>
|
|
static T& value(Synchronized_Value<T, Lock, Field_Lock>& synchronized) noexcept {
|
|
return synchronized.unsafe_value();
|
|
}
|
|
template <class T, Basic_Lock Lock, Basic_Lock Field_Lock>
|
|
static const T& value(const Synchronized_Value<T, Lock, Field_Lock>& synchronized) noexcept {
|
|
return synchronized.unsafe_value();
|
|
}
|
|
template <class T, Basic_Lock Lock, Basic_Lock Field_Lock>
|
|
static Lock& mutex(const Synchronized_Value<T, Lock, Field_Lock>& synchronized) noexcept {
|
|
return synchronized.mutex();
|
|
}
|
|
template <class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
|
|
static decltype(auto) value(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& synchronized) noexcept {
|
|
return synchronized.unsafe_value();
|
|
}
|
|
template <class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
|
|
static Lock& mutex(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& synchronized) noexcept {
|
|
return synchronized.mutex();
|
|
}
|
|
};
|
|
}
|
|
template <class Root, auto Member, Basic_Lock Lock, Basic_Lock Field_Lock = Lock>
|
|
using Synchronized_Member = Synchronized_Field<Root, Lock, Field_Lock, Member>;
|
|
}
|