零开销抽象
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user