Files
Adminive/backend/service/tests/synchronized_test.cpp
T
2026-08-07 09:25:55 +08:00

176 lines
7.0 KiB
C++

#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;
}