零开销抽象
This commit is contained in:
@@ -121,7 +121,7 @@ This preserves normal C++ member semantics, keeps raw object access available wh
|
||||
|
||||
`Type_Descriptor<T>` describes the **type**. `Property_Object<T>` adds state and behavior to an **instance**.
|
||||
|
||||
The schema contains the registered property tuple, object defaults and the default synchronization plan. A `Property_Object<T>` resolves that plan for each instance and owns its lock topology and mutex storage.
|
||||
The schema contains the registered property tuple, object defaults and the default synchronization plan. `Property_Object<T>` shares one resolved default lock topology per type and keeps only instance synchronization state that is actually required: real mutex storage for locking policies and a compact override layout only when an instance explicitly supplies `Property_Synchronization`.
|
||||
|
||||
This distinction matters for performance and architecture: structural description is type-level information; managed synchronization is instance-level state.
|
||||
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ struct Device : Property_Object<Device> {
|
||||
|
||||
`Type_Descriptor<T>` 描述的是**类型**;`Property_Object<T>` 管理的是**实例**。
|
||||
|
||||
Schema 保存注册属性列表、对象默认 Attribute 和默认同步计划。`Property_Object<T>` 在每个实例上解析同步计划,并持有自己的锁拓扑和 mutex 存储。
|
||||
Schema 保存注册属性列表、对象默认 Attribute 和默认同步计划。`Property_Object<T>` 对同一类型共享一份解析后的默认 lock topology;实例只保留真正需要的同步状态:真实锁策略需要 mutex storage,只有显式传入 `Property_Synchronization` 的实例才额外持有紧凑的覆盖布局。
|
||||
|
||||
这个边界必须长期保持:结构描述属于类型级;同步状态属于实例级。
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -74,6 +74,25 @@ struct structive::Type_Descriptor<Non_Copyable_Device> {
|
||||
);
|
||||
}
|
||||
};
|
||||
struct Lockless_Device : Property_Object<Lockless_Device, No_Lock_Policy> {
|
||||
Lockless_Device() = default;
|
||||
explicit Lockless_Device(Property_Synchronization synchronization) : Property_Object(std::move(synchronization)) {}
|
||||
int left{1};
|
||||
int right{2};
|
||||
};
|
||||
template <>
|
||||
struct structive::Type_Descriptor<Lockless_Device> {
|
||||
static auto get() {
|
||||
return object<Lockless_Device>(
|
||||
synchronization(sync_all_independent, sync_group("sum", "left", "right", "sum")),
|
||||
field < &Lockless_Device::left > (key < "left" >),
|
||||
field < &Lockless_Device::right > (key < "right" >),
|
||||
computed_property<Lockless_Device, int>([](const auto& view) {
|
||||
return view.template get<&Lockless_Device::left>() + view.template get<&Lockless_Device::right>();
|
||||
}, key < "sum" >)
|
||||
);
|
||||
}
|
||||
};
|
||||
template <class View>
|
||||
concept Has_Write_Temperature = requires(View view) {
|
||||
view.template write<&Device::temperature>(1);
|
||||
@@ -124,6 +143,14 @@ int main() {
|
||||
REQUIRE(shared_device.lock_slot<&Device::pressure>() == shared_device.lock_slot<&Device::min_speed>());
|
||||
Device grouped_device{property_synchronization<Device>(synchronization(sync_all_independent, sync_group < &Device::temperature, &Device::pressure > ("environment")))};
|
||||
REQUIRE(grouped_device.lock_slot<&Device::temperature>() == grouped_device.lock_slot<&Device::pressure>());
|
||||
Device shared_copy{shared_device};
|
||||
REQUIRE(shared_copy.lock_slot<&Device::temperature>() == shared_copy.lock_slot<&Device::pressure>());
|
||||
Device shared_move{std::move(shared_copy)};
|
||||
REQUIRE(shared_move.lock_slot<&Device::temperature>() == shared_move.lock_slot<&Device::pressure>());
|
||||
Device assignment_target;
|
||||
auto assignment_target_slot = assignment_target.lock_slot<&Device::pressure>();
|
||||
assignment_target = shared_device;
|
||||
REQUIRE(assignment_target.lock_slot<&Device::pressure>() == assignment_target_slot);
|
||||
device.external().write < &Device::temperature > (30);
|
||||
REQUIRE(device.external().read<&Device::temperature>() == 30);
|
||||
device.persistence().load < &Device::pressure > (101);
|
||||
@@ -151,6 +178,13 @@ int main() {
|
||||
REQUIRE(computed.external().read_key<"speed_span">() == 90);
|
||||
computed.write < &Computed_Device::min_speed > (20);
|
||||
REQUIRE(computed.external().read_key<"speed_span">() == 80);
|
||||
Lockless_Device lockless;
|
||||
REQUIRE(lockless.read<&Lockless_Device::left>() == 1);
|
||||
lockless.write < &Lockless_Device::right > (4);
|
||||
REQUIRE(lockless.read_key<"sum">() == 5);
|
||||
auto lockless_guard = lockless.lock_shared<&Lockless_Device::left, &Lockless_Device::right>();
|
||||
REQUIRE(lockless_guard.get<&Lockless_Device::left>() == 1);
|
||||
REQUIRE(lockless_guard.get<&Lockless_Device::right>() == 4);
|
||||
Non_Copyable_Device non_copyable;
|
||||
bool non_copyable_visited = false;
|
||||
non_copyable.external().for_each_readable_locked([&](auto, const auto&, const auto& value) {
|
||||
|
||||
+2
-2
@@ -407,7 +407,7 @@ auto policy = property_synchronization<Device>(
|
||||
);
|
||||
```
|
||||
|
||||
The schema remains the same; only the instance lock topology changes.
|
||||
The schema remains the same; only that instance uses an override lock topology. Default instances share one resolved topology per type, so the default plan is not re-resolved for every object.
|
||||
|
||||
## 15. Inspect resolved synchronization
|
||||
|
||||
@@ -628,7 +628,7 @@ struct Device : Property_Object<Device, No_Lock_Policy> {
|
||||
};
|
||||
```
|
||||
|
||||
`No_Lock_Policy` uses `Null_Shared_Mutex`; it removes real mutual exclusion and should only be selected when external ownership guarantees make that correct.
|
||||
`No_Lock_Policy` uses `Null_Shared_Mutex`; it removes real mutual exclusion and should only be selected when external ownership guarantees make that correct. Its default managed-object path stores no mutex array and performs no per-instance heap allocation for synchronization. A custom `Property_Synchronization` may still allocate a compact override topology because computed-property synchronization-domain checks must preserve the selected per-instance layout.
|
||||
|
||||
## 23. Raw object access
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ auto policy = property_synchronization<Device>(
|
||||
);
|
||||
```
|
||||
|
||||
Schema 本身不变,只改变该实例的 lock topology。
|
||||
Schema 本身不变,只有这个实例使用覆盖后的 lock topology。默认实例按类型共享一份解析结果,因此不会为每个对象重复解析默认同步计划。
|
||||
|
||||
## 15. 查看解析后的同步拓扑
|
||||
|
||||
@@ -628,7 +628,7 @@ struct Device : Property_Object<Device, No_Lock_Policy> {
|
||||
};
|
||||
```
|
||||
|
||||
`No_Lock_Policy` 使用 `Null_Shared_Mutex`,它取消真实互斥;只有外部所有权规则能够保证正确性时才应该使用。
|
||||
`No_Lock_Policy` 使用 `Null_Shared_Mutex`,它取消真实互斥;只有外部所有权规则能够保证正确性时才应该使用。默认 managed-object 路径不保存 mutex 数组,也不会为同步状态产生每实例堆分配。显式使用自定义 `Property_Synchronization` 时仍可能分配一份紧凑覆盖 topology,因为 computed property 的同步域检查必须保留该实例选择的布局。
|
||||
|
||||
## 23. Raw Object Access
|
||||
|
||||
|
||||
+4
-3
@@ -72,8 +72,9 @@ This is the structural definition of the type.
|
||||
|
||||
`Property_Object<T>` provides:
|
||||
|
||||
- resolved lock-slot topology;
|
||||
- per-instance mutex storage;
|
||||
- a type-shared resolved default lock-slot topology;
|
||||
- per-instance mutex storage only when the selected lock policy requires real mutexes;
|
||||
- a compact per-instance topology only for explicit synchronization overrides;
|
||||
- managed typed reads and writes;
|
||||
- capability views;
|
||||
- static and runtime multi-property guards;
|
||||
@@ -84,7 +85,7 @@ This is instance behavior, not schema identity.
|
||||
|
||||
### 3.3 Design rule
|
||||
|
||||
Do not move instance state into the schema, and do not make schema metadata depend on one particular instance-management policy.
|
||||
Do not move mutable instance synchronization state into the schema, and do not make schema metadata depend on one particular instance-management policy. Immutable topology derived from the type-level default plan may be shared across all instances of that type.
|
||||
|
||||
A future user may want to describe a large number of plain objects without paying per-object synchronization cost. The architecture should continue to leave that possibility open.
|
||||
|
||||
|
||||
@@ -72,8 +72,9 @@ Structive 把类型级描述和实例级管理分开。
|
||||
|
||||
`Property_Object<T>` 提供:
|
||||
|
||||
- 解析后的 lock slot 拓扑;
|
||||
- 每实例 mutex 存储;
|
||||
- 同一类型共享的默认解析 lock slot 拓扑;
|
||||
- 只有真实锁策略才需要的每实例 mutex 存储;
|
||||
- 只有显式同步覆盖实例才持有的紧凑 topology;
|
||||
- managed typed read/write;
|
||||
- capability view;
|
||||
- 静态与运行时多属性 guard;
|
||||
@@ -84,7 +85,7 @@ Structive 把类型级描述和实例级管理分开。
|
||||
|
||||
### 3.3 设计约束
|
||||
|
||||
不要把实例状态塞进 Schema,也不要让 Schema 必须依赖某一种特定的实例管理策略。
|
||||
不要把可变的实例同步状态塞进 Schema,也不要让 Schema 必须依赖某一种特定的实例管理策略。由类型级默认同步计划推导出的不可变 topology 可以由同类型所有实例共享。
|
||||
|
||||
以后完全可能有用户只想描述大量普通对象,却不愿意为每个对象承担同步状态成本。当前架构应该持续保留这种可能性。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user