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