Files
Structive/core/include/structive/property/synchronization.hpp
T
2026-08-07 20:20:46 +08:00

187 lines
8.1 KiB
C++

#pragma once
#include <array>
#include <cstddef>
#include <concepts>
#include <initializer_list>
#include <limits>
#include <span>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace structive {
/// Defines the default lock-domain topology for properties that actually require managed synchronization.
/// Stored intrinsic read-only properties are filtered out later and never receive a lock slot, regardless of this default.
enum class Synchronization_Default {
independent,
shared,
unsynchronized
};
/// Changes the default topology used before per-property and group overrides are applied.
struct Sync_Default_Rule {
Synchronization_Default value{Synchronization_Default::independent};
};
/// Runtime-key override for one property. The key is resolved against the schema when the plan is materialized.
/// `independent` forces a dedicated domain; `unsynchronized` deliberately removes the property from real locking.
struct Sync_Property_Rule {
enum class Mode {
independent,
unsynchronized
};
Mode mode{Mode::independent};
std::string property;
};
/// Runtime-key declaration that places multiple properties in one consistency domain.
/// Group membership affects only properties that require synchronization; intrinsic read-only stored properties remain lock-free.
struct Sync_Group_Rule {
std::string name;
std::vector<std::string> properties;
};
/// Compile-time member-pointer form of a single-property synchronization override.
template <auto Member, Sync_Property_Rule::Mode Mode>
struct Sync_Member_Rule {
static constexpr auto member = Member;
static constexpr auto mode = Mode;
};
/// Compile-time member-pointer form of a synchronization group. Member validity is checked against the target schema during materialization.
template <auto... Members>
struct Sync_Member_Group_Rule {
static_assert(sizeof...(Members) > 0);
std::string name;
};
inline constexpr Sync_Default_Rule sync_all_independent{Synchronization_Default::independent};
inline constexpr Sync_Default_Rule sync_all_shared{Synchronization_Default::shared};
inline constexpr Sync_Default_Rule sync_all_unsynchronized{Synchronization_Default::unsynchronized};
inline Sync_Property_Rule sync_independent(std::string_view property) {
return {Sync_Property_Rule::Mode::independent, std::string(property)};
}
inline Sync_Property_Rule sync_unsynchronized(std::string_view property) {
return {Sync_Property_Rule::Mode::unsynchronized, std::string(property)};
}
template <auto Member>
constexpr auto sync_independent() {
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::independent>{};
}
template <auto Member>
constexpr auto sync_unsynchronized() {
return Sync_Member_Rule<Member, Sync_Property_Rule::Mode::unsynchronized>{};
}
template <auto... Members>
Sync_Member_Group_Rule<Members...> sync_group(std::string_view name) {
return {std::string(name)};
}
template <class... Properties>
Sync_Group_Rule sync_group(std::string_view name, Properties&&... properties) requires (std::convertible_to<Properties, std::string_view> && ...) {
Sync_Group_Rule rule;
rule.name = name;
rule.properties.reserve(sizeof...(Properties));
(rule.properties.emplace_back(std::string_view(std::forward<Properties>(properties))), ...);
return rule;
}
/// Runtime-materialized synchronization description.
/// A plan describes topology, not mutex objects: resolution converts keys and groups into compact lock slots for one schema.
/// Invalid references, duplicate property configuration, empty groups and duplicate group names are rejected while resolving the plan.
/// The schema default plan is resolved once per object type; a `Property_Synchronization` override resolves a separate compact layout only for that instance.
class Synchronization_Plan {
Synchronization_Default default_mode_{Synchronization_Default::independent};
std::vector<Sync_Property_Rule> property_rules_;
std::vector<Sync_Group_Rule> group_rules_;
public:
using synchronization_plan_tag = void;
Synchronization_Plan() = default;
explicit Synchronization_Plan(Synchronization_Default mode) : default_mode_(mode) {}
Synchronization_Default default_mode() const noexcept {
return default_mode_;
}
const auto& property_rules() const noexcept {
return property_rules_;
}
const auto& group_rules() const noexcept {
return group_rules_;
}
Synchronization_Plan& set_default(Synchronization_Default mode) {
default_mode_ = mode;
return *this;
}
Synchronization_Plan& independent(std::string_view property) {
property_rules_.push_back(sync_independent(property));
return *this;
}
Synchronization_Plan& unsynchronized(std::string_view property) {
property_rules_.push_back(sync_unsynchronized(property));
return *this;
}
Synchronization_Plan& group(std::string_view name, std::span<const std::string_view> properties) {
Sync_Group_Rule rule;
rule.name = name;
rule.properties.reserve(properties.size());
for (auto property : properties) {
rule.properties.emplace_back(property);
}
group_rules_.push_back(std::move(rule));
return *this;
}
Synchronization_Plan& group(std::string_view name, std::initializer_list<std::string_view> properties) {
return group(name, std::span<const std::string_view>{properties.begin(), properties.size()});
}
Synchronization_Plan& apply(Sync_Default_Rule rule) {
default_mode_ = rule.value;
return *this;
}
Synchronization_Plan& apply(Sync_Property_Rule rule) {
property_rules_.push_back(std::move(rule));
return *this;
}
Synchronization_Plan& apply(Sync_Group_Rule rule) {
group_rules_.push_back(std::move(rule));
return *this;
}
};
template <class Type>
concept Synchronization_Plan_Type = requires {
typename Type::synchronization_plan_tag;
};
template <class Type>
concept Runtime_Synchronization_Rule = std::same_as<std::remove_cvref_t<Type>, Sync_Default_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Property_Rule> || std::same_as<std::remove_cvref_t<Type>, Sync_Group_Rule>;
template <class... Rules>
struct Synchronization_Spec {
using synchronization_spec_tag = void;
std::tuple<Rules...> rules;
};
template <class Type>
concept Synchronization_Spec_Type = requires {
typename Type::synchronization_spec_tag;
};
template <class Type>
concept Synchronization_Source_Type = Synchronization_Plan_Type<Type> || Synchronization_Spec_Type<Type>;
/// Builds synchronization configuration without forcing every caller onto runtime strings.
/// If every rule is already runtime-shaped, this returns `Synchronization_Plan` directly. If any rule carries compile-time member pointers, it returns a typed specification that is later materialized against the concrete schema.
template <class... Rules>
auto synchronization(Rules&&... rules) {
if constexpr ((Runtime_Synchronization_Rule<Rules> && ...)) {
Synchronization_Plan plan;
(plan.apply(std::forward<Rules>(rules)), ...);
return plan;
} else {
return Synchronization_Spec<std::decay_t<Rules>...>{{std::forward<Rules>(rules)...}};
}
}
/// Compact result after applying defaults, overrides, groups and intrinsic property capabilities.
/// `lock_slots[i]` identifies the mutex domain for property `i`; `unsynchronized_slot` means no mutex exists for that property.
/// `lock_count` is the number of actual logical mutex domains after read-only filtering and group deduplication.
template <std::size_t Property_Count>
struct Resolved_Synchronization_Plan {
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
std::array<std::size_t, Property_Count> lock_slots{};
std::size_t lock_count{};
constexpr std::size_t slot(std::size_t property_index) const noexcept {
return lock_slots[property_index];
}
constexpr bool uses_lock(std::size_t property_index) const noexcept {
return slot(property_index) != unsynchronized_slot;
}
};
}