修复bug

This commit is contained in:
2026-08-08 20:47:14 +08:00
parent 8becd0bf94
commit 1fd375a23b
2 changed files with 0 additions and 629 deletions
@@ -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>;
}
-175
View File
@@ -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;
}