零开销抽象

This commit is contained in:
2026-08-07 16:51:40 +08:00
parent 1e903dfe1e
commit 1ddd2799f2
8 changed files with 164 additions and 46 deletions
@@ -42,6 +42,43 @@ template <class Policy>
concept Synchronization_Policy = requires {
typename Policy::mutex_type;
} && Shared_Lockable<typename Policy::mutex_type>;
namespace detail {
template <class Mutex, bool Stores_Mutexes = !std::same_as<Mutex, Null_Shared_Mutex>>
class Mutex_Storage;
template <class Mutex>
class Mutex_Storage<Mutex, true> {
std::unique_ptr<Mutex[]> mutexes_;
public:
Mutex_Storage() = default;
explicit Mutex_Storage(std::size_t count) : mutexes_(count ? std::make_unique<Mutex[]>(count) : nullptr) {}
void reset(std::size_t count) {
mutexes_ = count ? std::make_unique<Mutex[]>(count) : nullptr;
}
Mutex& get(std::size_t index) noexcept {
return mutexes_[index];
}
Mutex& get(std::size_t index) const noexcept {
return mutexes_[index];
}
};
template <class Mutex>
class Mutex_Storage<Mutex, false> {
static Mutex& mutex() noexcept {
static Mutex value;
return value;
}
public:
Mutex_Storage() = default;
explicit Mutex_Storage(std::size_t) {}
void reset(std::size_t) {}
Mutex& get(std::size_t) noexcept {
return mutex();
}
Mutex& get(std::size_t) const noexcept {
return mutex();
}
};
}
enum class Managed_Access_Mode {
internal,
external,
@@ -120,6 +157,8 @@ public:
using object_type = Derived;
using mutex_type = typename Lock_Policy::mutex_type;
private:
static constexpr bool uses_real_mutexes = !std::same_as<mutex_type, Null_Shared_Mutex>;
struct Empty_Guard_Locks {};
struct Dynamic_Lock_Targets {
std::vector<std::size_t> slots;
std::vector<std::size_t> unsynchronized_properties;
@@ -131,23 +170,43 @@ private:
std::array<std::size_t, Capacity> unsynchronized_properties{};
std::size_t unsynchronized_count{};
};
std::vector<std::size_t> lock_slots_;
std::size_t lock_count_{};
std::unique_ptr<mutex_type[]> locks_;
std::unique_ptr<std::size_t[]> custom_lock_layout_;
[[no_unique_address]] detail::Mutex_Storage<mutex_type> mutex_storage_;
static const auto& default_resolved_synchronization() {
static const auto resolved = resolve_synchronization_plan(type_descriptor<Derived>(), type_descriptor<Derived>().synchronization_plan());
return resolved;
}
template <class Schema>
void initialize(const Schema& schema, const Synchronization_Plan& plan) {
auto resolved = resolve_synchronization_plan(schema, plan);
lock_slots_.assign(resolved.lock_slots.begin(), resolved.lock_slots.end());
lock_count_ = resolved.lock_count;
locks_ = lock_count_ ? std::make_unique<mutex_type[]>(lock_count_) : nullptr;
auto layout = std::make_unique<std::size_t[]>(Schema::property_count + 1);
layout[0] = resolved.lock_count;
std::copy(resolved.lock_slots.begin(), resolved.lock_slots.end(), layout.get() + 1);
custom_lock_layout_ = std::move(layout);
mutex_storage_.reset(resolved.lock_count);
}
void copy_synchronization_from(const Property_Object& other) {
lock_slots_ = other.lock_slots_;
lock_count_ = other.lock_count_;
locks_ = lock_count_ ? std::make_unique<mutex_type[]>(lock_count_) : nullptr;
using Schema = type_descriptor_schema_t<Derived>;
if (other.custom_lock_layout_) {
auto layout = std::make_unique<std::size_t[]>(Schema::property_count + 1);
std::copy_n(other.custom_lock_layout_.get(), Schema::property_count + 1, layout.get());
custom_lock_layout_ = std::move(layout);
} else {
custom_lock_layout_.reset();
}
mutex_storage_.reset(lock_count());
}
std::size_t lock_count() const noexcept {
return custom_lock_layout_ ? custom_lock_layout_[0] : default_resolved_synchronization().lock_count;
}
std::size_t slot(std::size_t index) const noexcept {
return lock_slots_[index];
return custom_lock_layout_ ? custom_lock_layout_[index + 1] : default_resolved_synchronization().slot(index);
}
mutex_type& mutex(std::size_t index) noexcept {
return mutex_storage_.get(index);
}
mutex_type& mutex(std::size_t index) const noexcept {
return mutex_storage_.get(index);
}
template <std::size_t Capacity>
static void sort_unique_prefix(std::array<std::size_t, Capacity>& values, std::size_t& count) {
@@ -303,25 +362,36 @@ private:
auto read_one() const {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_read_allowed_v<Mode, Schema, Index>);
using Value = typename Schema::template property_type<Index>::value_type;
using Property = typename Schema::template property_type<Index>;
using Value = typename Property::value_type;
if constexpr (!uses_real_mutexes && !Property::accessor_type::synchronized_view_read) {
return Value(read_unlocked<Index>(*this));
}
auto lock_slot = slot(Index);
Single_Read_View<Mode> view{*this, Index, lock_slot};
if constexpr (!uses_real_mutexes) {
return Value(read_unlocked<Index>(view));
}
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
return Value(read_unlocked<Index>(view));
}
std::shared_lock lock{locks_[lock_slot]};
std::shared_lock lock{mutex(lock_slot)};
return Value(read_unlocked<Index>(view));
}
template <Managed_Access_Mode Mode, std::size_t Index, class Value>
void write_one(Value&& value) {
using Schema = type_descriptor_schema_t<Derived>;
static_assert(property_write_allowed_v<Mode, Schema, Index>);
if constexpr (!uses_real_mutexes) {
write_unlocked<Index>(std::forward<Value>(value));
return;
}
auto lock_slot = slot(Index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
write_unlocked<Index>(std::forward<Value>(value));
return;
}
std::unique_lock lock{locks_[lock_slot]};
std::unique_lock lock{mutex(lock_slot)};
write_unlocked<Index>(std::forward<Value>(value));
}
public:
@@ -330,7 +400,7 @@ public:
const Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> unsynchronized_properties_;
std::vector<std::shared_lock<mutex_type>> locks_;
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::vector<std::shared_lock<mutex_type>>, Empty_Guard_Locks> locks_;
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -340,9 +410,11 @@ public:
}
friend class Property_Object;
Read_Guard(const Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->locks_[lock_slot]);
if constexpr (uses_real_mutexes) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->mutex(lock_slot));
}
}
}
public:
@@ -376,7 +448,7 @@ public:
Property_Object* owner_{};
std::vector<std::size_t> slots_;
std::vector<std::size_t> unsynchronized_properties_;
std::vector<std::unique_lock<mutex_type>> locks_;
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::vector<std::unique_lock<mutex_type>>, Empty_Guard_Locks> locks_;
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -386,9 +458,11 @@ public:
}
friend class Property_Object;
Write_Guard(Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->locks_[lock_slot]);
if constexpr (uses_real_mutexes) {
locks_.reserve(slots_.size());
for (auto lock_slot : slots_) {
locks_.emplace_back(owner_->mutex(lock_slot));
}
}
}
public:
@@ -445,7 +519,7 @@ public:
class Static_Read_Guard {
const Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
std::array<std::shared_lock<mutex_type>, Capacity> locks_{};
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::array<std::shared_lock<mutex_type>, Capacity>, Empty_Guard_Locks> locks_{};
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -455,8 +529,10 @@ public:
}
friend class Property_Object;
Static_Read_Guard(const Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::shared_lock<mutex_type>{owner_->locks_[targets_.slots[index]]};
if constexpr (uses_real_mutexes) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::shared_lock<mutex_type>{owner_->mutex(targets_.slots[index])};
}
}
}
public:
@@ -489,7 +565,7 @@ public:
class Static_Write_Guard {
Property_Object* owner_{};
Static_Lock_Targets<Capacity> targets_;
std::array<std::unique_lock<mutex_type>, Capacity> locks_{};
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::array<std::unique_lock<mutex_type>, Capacity>, Empty_Guard_Locks> locks_{};
bool holds(std::size_t index) const {
auto lock_slot = owner_->slot(index);
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
@@ -499,8 +575,10 @@ public:
}
friend class Property_Object;
Static_Write_Guard(Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::unique_lock<mutex_type>{owner_->locks_[targets_.slots[index]]};
if constexpr (uses_real_mutexes) {
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
locks_[index] = std::unique_lock<mutex_type>{owner_->mutex(targets_.slots[index])};
}
}
}
public:
@@ -617,10 +695,12 @@ private:
callback(context, property_index, key, typeid(Value), std::addressof(value));
}
};
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
if constexpr (!uses_real_mutexes) {
emit();
} else if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
emit();
} else {
std::shared_lock lock{locks_[lock_slot]};
std::shared_lock lock{mutex(lock_slot)};
emit();
}
result = Runtime_Access_Result::ok;
@@ -688,10 +768,7 @@ private:
return &value;
}
protected:
Property_Object() : Property_Object_Base(runtime_interface()) {
const auto& schema = type_descriptor<Derived>();
initialize(schema, schema.synchronization_plan());
}
Property_Object() : Property_Object_Base(runtime_interface()), mutex_storage_(default_resolved_synchronization().lock_count) {}
explicit Property_Object(Property_Synchronization synchronization) : Property_Object_Base(runtime_interface()) {
initialize(type_descriptor<Derived>(), synchronization.plan);
}
@@ -713,7 +790,12 @@ public:
return type_descriptor<Derived>();
}
Resolved_Synchronization_View resolved_synchronization() const noexcept {
return {lock_slots_, lock_count_};
using Schema = type_descriptor_schema_t<Derived>;
if (custom_lock_layout_) {
return {{custom_lock_layout_.get() + 1, Schema::property_count}, custom_lock_layout_[0]};
}
const auto& resolved = default_resolved_synchronization();
return {resolved.lock_slots, resolved.lock_count};
}
Derived& unsafe_object() noexcept {
return static_cast<Derived&>(*this);