diff --git a/backend/library/include/adminive/synchronized.hpp b/backend/library/include/adminive/synchronized.hpp deleted file mode 100644 index c651c95..0000000 --- a/backend/library/include/adminive/synchronized.hpp +++ /dev/null @@ -1,454 +0,0 @@ -#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; -} diff --git a/backend/service/tests/synchronized_test.cpp b/backend/service/tests/synchronized_test.cpp deleted file mode 100644 index 177fc00..0000000 --- a/backend/service/tests/synchronized_test.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "adminive/adapters/nlohmann_json.hpp" -#include "adminive/http.hpp" -#include -#include -#include -#include -#include -#include -namespace synchronized_test { -using Json = nlohmann::json; -struct Section_Config { - int editable_value{1}; - int immutable_value{2}; - int forced_value{3}; - int unlocked_editable_value{4}; -}; -struct Root_Config { - Section_Config section; - int immutable_value{5}; -}; -struct Concurrent_Config { - int first{}; - int second{}; - int immutable_value{7}; -}; -struct Probe_Lock { - static inline int exclusive_locks{}; - static inline int shared_locks{}; - void lock() noexcept { - ++exclusive_locks; - } - void unlock() noexcept {} - void lock_shared() noexcept { - ++shared_locks; - } - void unlock_shared() noexcept {} - static void reset() noexcept { - exclusive_locks = 0; - shared_locks = 0; - } -}; -} -namespace adminive { -template <> -struct Type_Descriptor { - static auto get() { - using T = synchronized_test::Section_Config; - return object("section", "Section", ADMINIVE_FIELD(T, editable_value).editable(), ADMINIVE_FIELD(T, immutable_value), ADMINIVE_FIELD(T, forced_value).locked(), ADMINIVE_FIELD(T, unlocked_editable_value).editable().locked(false)); - } -}; -template <> -struct Type_Descriptor { - static auto get() { - using T = synchronized_test::Root_Config; - return object("root", "Root", ADMINIVE_FIELD(T, section).locked(false), ADMINIVE_FIELD(T, immutable_value)); - } -}; -template <> -struct Type_Descriptor { - static auto get() { - using T = synchronized_test::Concurrent_Config; - return object("concurrent", "Concurrent", ADMINIVE_FIELD(T, first).editable(), ADMINIVE_FIELD(T, second).editable(), ADMINIVE_FIELD(T, immutable_value)); - } -}; -} -int main() { - using namespace synchronized_test; - static_assert(!adminive::Synchronized_Result); - static_assert(!adminive::Synchronized_Result>); - adminive::Synchronized_Value guarded; - Probe_Lock::reset(); - const auto immutable_root = guarded.member<&Root_Config::immutable_value>().read([](const int& value) { - return value; - }); - assert(immutable_root == 5); - assert(Probe_Lock::exclusive_locks == 0); - assert(Probe_Lock::shared_locks == 1); - auto section = guarded.member<&Root_Config::section>(); - Probe_Lock::reset(); - const auto immutable_child = section.member<&Section_Config::immutable_value>().read([](const int& value) { - return value; - }); - assert(immutable_child == 2); - assert(Probe_Lock::exclusive_locks == 0); - assert(Probe_Lock::shared_locks == 1); - Probe_Lock::reset(); - section.member<&Section_Config::editable_value>().write([](int& value) { - value = 7; - }); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks >= 2); - Probe_Lock::reset(); - section.member<&Section_Config::forced_value>().write([](int& value) { - value = 8; - }); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks >= 2); - Probe_Lock::reset(); - section.member<&Section_Config::unlocked_editable_value>().write([](int& value) { - value = 9; - }); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks == 0); - adminive::Resource_Service section_service(section, "/section"); - Probe_Lock::reset(); - const auto section_response = section_service.update_response(R"({"editable_value":10})"); - assert(section_response.status == 200); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks == 1); - adminive::Resource_Service root_scope_service(section, "/section-root", {}, adminive::Resource_Lock_Scope::root); - Probe_Lock::reset(); - const auto root_scope_response = root_scope_service.update_response(R"({"editable_value":11})"); - assert(root_scope_response.status == 200); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks == 0); - Probe_Lock::reset(); - const Json section_json = adminive::to_json(section); - assert(section_json.at("editable_value") == 11); - assert(section_json.at("forced_value") == 8); - assert(section_json.at("unlocked_editable_value") == 9); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks == 1); - Probe_Lock::reset(); - const Json root_json = adminive::to_json(guarded); - assert(root_json.at("section").at("editable_value") == 11); - assert(Probe_Lock::exclusive_locks == 1); - assert(Probe_Lock::shared_locks == 0); - const auto root_descriptor = adminive::to_descriptor_json(); - assert(root_descriptor.at("fields").at(0).at("lock_mode") == "disabled"); - assert(root_descriptor.at("fields").at(0).at("synchronized") == true); - const auto descriptor = adminive::to_descriptor_json(); - assert(descriptor.at("protocol_version") == 3); - assert(descriptor.at("fields").at(0).at("synchronized") == true); - assert(descriptor.at("fields").at(1).at("synchronized") == false); - assert(descriptor.at("fields").at(2).at("lock_mode") == "enabled"); - assert(descriptor.at("fields").at(3).at("lock_mode") == "disabled"); - adminive::Synchronized_Value unlocked; - adminive::Resource_Service unlocked_service(unlocked, "/unlocked"); - const auto response = unlocked_service.update_response(R"({"editable_value":11})"); - assert(response.status == 200); - assert(unlocked.snapshot().editable_value == 11); - adminive::Synchronized_Value concurrent; - std::atomic start{}; - auto first_writer = std::thread([&] { - while(!start.load(std::memory_order_acquire)) {} - for(int index = 0; index < 5000; ++index) { - concurrent.member<&Concurrent_Config::first>().write([index](int& value) { - value = index; - }); - } - }); - auto second_writer = std::thread([&] { - while(!start.load(std::memory_order_acquire)) {} - for(int index = 0; index < 5000; ++index) { - concurrent.member<&Concurrent_Config::second>().write([index](int& value) { - value = index; - }); - } - }); - auto serializer = std::thread([&] { - while(!start.load(std::memory_order_acquire)) {} - for(int index = 0; index < 1000; ++index) { - const Json value = adminive::to_json(concurrent); - assert(value.at("immutable_value") == 7); - } - }); - start.store(true, std::memory_order_release); - first_writer.join(); - second_writer.join(); - serializer.join(); - const auto concurrent_snapshot = concurrent.snapshot(); - assert(concurrent_snapshot.first == 4999); - assert(concurrent_snapshot.second == 4999); - return 0; -}