修复bug
This commit is contained in:
@@ -1,454 +0,0 @@
|
||||
#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>;
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
#include "adminive/adapters/nlohmann_json.hpp"
|
||||
#include "adminive/http.hpp"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
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<synchronized_test::Section_Config> {
|
||||
static auto get() {
|
||||
using T = synchronized_test::Section_Config;
|
||||
return object<T>("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<synchronized_test::Root_Config> {
|
||||
static auto get() {
|
||||
using T = synchronized_test::Root_Config;
|
||||
return object<T>("root", "Root", ADMINIVE_FIELD(T, section).locked(false), ADMINIVE_FIELD(T, immutable_value));
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct Type_Descriptor<synchronized_test::Concurrent_Config> {
|
||||
static auto get() {
|
||||
using T = synchronized_test::Concurrent_Config;
|
||||
return object<T>("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<std::string_view>);
|
||||
static_assert(!adminive::Synchronized_Result<std::reference_wrapper<int>>);
|
||||
adminive::Synchronized_Value<Root_Config, Probe_Lock> 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_Config, Json, Probe_Lock> 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<Section_Config, Json, Probe_Lock> 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<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<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<Json, Root_Config>();
|
||||
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<Json, Section_Config>();
|
||||
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<Section_Config, adminive::Empty_Lock> unlocked;
|
||||
adminive::Resource_Service<Section_Config, Json, adminive::Empty_Lock> 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_Config> concurrent;
|
||||
std::atomic<bool> 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<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;
|
||||
}
|
||||
Reference in New Issue
Block a user