176 lines
7.0 KiB
C++
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;
|
|
}
|