载入中...
搜索中...
未找到
RuntimeRegistry.h
浏览该文件的文档.
1#pragma once
2
12#include "common/Export.h"
13#include "common/Result.h"
15
16#include <atomic>
17#include <cstdint>
18#include <memory>
19#include <optional>
20#include <type_traits>
21#include <utility>
22#include <vector>
23
24namespace eve::script {
25
32template <class T>
33using Owned = std::unique_ptr<T>;
40template <class T>
42class Borrowed {
43public:
45 constexpr Borrowed() noexcept = default;
47 explicit constexpr Borrowed(T* value, std::uint64_t ownerEpoch = 0) noexcept
49 : value_(value), ownerEpoch_(ownerEpoch) {}
51 explicit constexpr Borrowed(T& value, std::uint64_t ownerEpoch = 0) noexcept
53 : value_(&value), ownerEpoch_(ownerEpoch) {}
54
65 [[nodiscard]] constexpr T* get() const noexcept { return value_; }
67 [[nodiscard]] constexpr bool isBound() const noexcept { return value_ != nullptr; }
69 [[nodiscard]] constexpr std::uint64_t ownerEpoch() const noexcept { return ownerEpoch_; }
71 constexpr T* operator->() const noexcept { return value_; }
73 constexpr T& operator*() const noexcept { return *value_; }
74
75private:
76 T* value_ = nullptr;
77 std::uint64_t ownerEpoch_ = 0;
78};
79
80
89template <class Tag>
93 std::uint64_t ownerEpoch = 0;
94
96 [[nodiscard]] constexpr bool isValid() const noexcept { return handle.isValid() && ownerEpoch != 0; }
98 [[nodiscard]] constexpr std::uint64_t packed() const noexcept { return handle.packed(); }
99 friend constexpr bool operator==(const RuntimeHandleRef&, const RuntimeHandleRef&) noexcept = default;
100};
101
102namespace detail {
103
105using OwnedInstanceDestroy = void (*)(void*) noexcept;
106
108template <class T>
110void ownedInstanceDestroy(void* pointer) noexcept {
111 delete static_cast<T*>(pointer);
112}
113
116 std::uint32_t index = 0;
117 std::uint32_t generation = 0;
118};
119
135public:
137 explicit RuntimeSlotStore(OwnedInstanceDestroy destroy) noexcept : destroy_(destroy), ownerEpoch_(nextEpoch()) {}
138
141 // A RuntimePin holds this store's address, so relocating the store would leave that pin
142 // unpinning the moved-from store while the payload lives in the destination. The store
143 // must therefore stay at a fixed address for its whole lifetime.
148
156 [[nodiscard]] EVENGINE_API Result<RuntimeSlotCoordinates> emplace(void* object);
157
159 [[nodiscard]] EVENGINE_API void* resolve(std::uint32_t index, std::uint32_t generation,
160 std::uint64_t ownerEpoch) const noexcept;
161
163 [[nodiscard]] EVENGINE_API Result<void> erase(std::uint32_t index, std::uint32_t generation,
164 std::uint64_t ownerEpoch);
165
167 [[nodiscard]] EVENGINE_API bool isStale(std::uint32_t index, std::uint32_t generation,
168 std::uint64_t ownerEpoch) const noexcept;
169
179 [[nodiscard]] EVENGINE_API Result<void*> pin(std::uint32_t index, std::uint32_t generation,
180 std::uint64_t ownerEpoch);
181
190 EVENGINE_API void unpin(std::uint32_t index, std::uint64_t ownerEpoch) noexcept;
191
193 EVENGINE_API void clear();
194
205 template <class F>
207 void forEachLive(F&& visit) {
208 for (std::uint32_t index = 0; index < slots_.size(); ++index) {
209 void* object = slots_[index].object;
210 if (object == nullptr) continue;
212 visit(index, object);
213 }
214 }
215
217 template <class F>
219 void forEachLive(F&& visit) const {
220 for (std::uint32_t index = 0; index < slots_.size(); ++index) {
221 const void* object = slots_[index].object;
222 if (object == nullptr) continue;
224 visit(index, object);
225 }
226 }
227
229 [[nodiscard]] std::uint64_t ownerEpoch() const noexcept { return ownerEpoch_; }
230
231private:
232 struct Slot {
233 std::uint32_t generation = 1;
234 bool retired = false;
236 bool orphaned = false;
238 std::uint32_t pins = 0;
239 void* object = nullptr;
240 };
241
242 [[nodiscard]] std::optional<std::uint32_t> findSlot(std::uint32_t index, std::uint32_t generation,
243 std::uint64_t ownerEpoch) const noexcept;
244 [[nodiscard]] static bool coordinatesValid(std::uint32_t index, std::uint32_t generation) noexcept;
246 [[nodiscard]] static std::optional<std::uint32_t> nextGeneration(std::uint32_t current) noexcept;
247 [[nodiscard]] EVENGINE_API static std::uint64_t nextEpoch() noexcept;
248 void destroySlot(Slot& slot) noexcept;
249
250 OwnedInstanceDestroy destroy_;
251 std::uint64_t ownerEpoch_;
252 std::vector<Slot> slots_;
253 std::vector<std::uint32_t> freeSlots_;
254
255 inline static std::atomic<std::uint64_t> nextEpoch_{1};
256};
257
258} // namespace detail
259
260template <class T, class Tag>
261class RuntimeObjectRegistry;
262
274template <class T, class Tag>
277public:
279
281 RuntimePin() = default;
282 RuntimePin(const RuntimePin&) = delete;
283 RuntimePin& operator=(const RuntimePin&) = delete;
284
286 RuntimePin(RuntimePin&& other) noexcept
288 : store_(other.store_), reference_(other.reference_), object_(other.object_) {
289 other.store_ = nullptr;
290 other.object_ = nullptr;
291 }
292
294 RuntimePin& operator=(RuntimePin&& other) noexcept {
295 if (this != &other) {
297 release();
298 store_ = other.store_;
299 reference_ = other.reference_;
300 object_ = other.object_;
301 other.store_ = nullptr;
302 other.object_ = nullptr;
303 }
304 return *this;
305 }
306
308 ~RuntimePin() { release(); }
309
311 [[nodiscard]] bool isBound() const noexcept { return store_ != nullptr && object_ != nullptr; }
312
314 [[nodiscard]] const Ref& reference() const noexcept { return reference_; }
315
317 [[nodiscard]] T* get() const noexcept { return static_cast<T*>(object_); }
318
320 [[nodiscard]] Borrowed<T> borrow() const noexcept { return Borrowed<T>(get(), reference_.ownerEpoch); }
321
323 [[nodiscard]] T* operator->() const noexcept { return get(); }
324
326 [[nodiscard]] T& operator*() const noexcept { return *get(); }
327
328private:
329 friend class RuntimeObjectRegistry<T, Tag>;
330
331 RuntimePin(detail::RuntimeSlotStore* store, Ref reference, void* object) noexcept
332 : store_(store), reference_(reference), object_(object) {}
333
334 void release() noexcept {
335 if (store_ != nullptr) store_->unpin(reference_.handle.index(), reference_.ownerEpoch);
336 store_ = nullptr;
337 object_ = nullptr;
338 }
339
340 detail::RuntimeSlotStore* store_ = nullptr;
341 Ref reference_{};
342 void* object_ = nullptr;
343};
344
357template <class T, class Tag>
360public:
363
365 RuntimeObjectRegistry() : store_(std::make_unique<detail::RuntimeSlotStore>(&detail::ownedInstanceDestroy<T>)) {}
368 // The store lives behind a stable heap address, so moving the registry relocates only
369 // that pointer: pins issued before the move keep referencing the same store and stay
370 // valid, which is why the registry may be movable while RuntimeSlotStore is not.
374 RuntimeObjectRegistry& operator=(RuntimeObjectRegistry&&) noexcept = default;
375
381 [[nodiscard]] eve::Result<Ref> emplace(Owned<T> object) {
382 if (!object) {
385 "runtime registry cannot own a null object", {}, {},
386 "runtime.registry"));
387 }
388 auto slot = store_->emplace(static_cast<void*>(object.get()));
389 if (!slot.ok()) return eve::Result<Ref>::failure(slot.status());
390 const detail::RuntimeSlotCoordinates coordinates = slot.value();
391 object.release();
393 return eve::Result<Ref>::success(Ref{Handle(coordinates.index, coordinates.generation), store_->ownerEpoch()});
394 }
395
407 [[nodiscard]] Borrowed<T> resolve(Ref ref) noexcept {
408 return Borrowed<T>(
409 static_cast<T*>(store_->resolve(ref.handle.index(), ref.handle.generation(), ref.ownerEpoch)),
410 store_->ownerEpoch());
411 }
412
414 [[nodiscard]] Borrowed<const T> resolve(Ref ref) const noexcept {
415 return Borrowed<const T>(
416 static_cast<const T*>(store_->resolve(ref.handle.index(), ref.handle.generation(), ref.ownerEpoch)),
417 store_->ownerEpoch());
418 }
419
426 [[nodiscard]] eve::Result<void> erase(Ref ref) {
427 return store_->erase(ref.handle.index(), ref.handle.generation(), ref.ownerEpoch);
428 }
429
440 auto object = store_->pin(ref.handle.index(), ref.handle.generation(), ref.ownerEpoch);
441 if (!object.ok()) return eve::Result<RuntimePin<T, Tag>>::failure(object.status());
442 return eve::Result<RuntimePin<T, Tag>>::success(RuntimePin<T, Tag>(store_.get(), ref, object.value()));
443 }
444
446 [[nodiscard]] bool isStale(Ref ref) const noexcept {
447 return store_->isStale(ref.handle.index(), ref.handle.generation(), ref.ownerEpoch);
448 }
449
463 template <class F>
465 void forEachLive(F&& visit) {
466 store_->forEachLive(
467 [&](std::uint32_t index, void* object) { visit(index, static_cast<T&>(*static_cast<T*>(object))); });
468 }
469
471 template <class F>
473 void forEachLive(F&& visit) const {
474 store_->forEachLive([&](std::uint32_t index, const void* object) {
476 visit(index, static_cast<const T&>(*static_cast<const T*>(object)));
477 });
478 }
479
481 void clear() { store_->clear(); }
482
484 [[nodiscard]] std::uint64_t ownerEpoch() const noexcept { return store_->ownerEpoch(); }
485
486private:
488 std::unique_ptr<detail::RuntimeSlotStore> store_;
489};
490
491} // namespace eve::script
double value
#define EVENGINE_API
宿主(eve / libmain)导出宏;插件从进程导入同一批符号。
Definition Export.h:31
bool retired
wgpu::PopErrorScopeStatus status
void(* destroy)(void *)
std::uint32_t generation
std::function< eve::Result< void >()> release
Definition Procgen.cpp:86
int detail
Move-only, checked operation results for the common layer.
Strong generation-qualified handles for non-ECS runtime registries.
double current
uint32_t index
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Process-local slot identity tagged by the registry that owns it.
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
Non-owning C++ observation with no implicit lifetime extension.
constexpr T * operator->() const noexcept
Pointer-like access to the borrowed object.
constexpr Borrowed() noexcept=default
Constructs a Borrowed.
constexpr std::uint64_t ownerEpoch() const noexcept
Returns the epoch of the owner that produced this observation.
constexpr bool isBound() const noexcept
Returns whether the observation currently names an object.
constexpr T * get() const noexcept
Returns the observed pointer, or nullptr when unbound.
constexpr Borrowed(T &value, std::uint64_t ownerEpoch=0) noexcept
Constructs a Borrowed.
constexpr T & operator*() const noexcept
Dereferences the borrowed object; the caller owns validity proof.
Slot/generation registry whose objects are exclusively unique-owned.
eve::Result< void > erase(Ref ref)
Destroys the object identified by a live handle.
bool isStale(Ref ref) const noexcept
Reports whether a non-invalid handle can no longer resolve.
std::uint64_t ownerEpoch() const noexcept
Returns the non-reusable lifetime epoch of this registry.
void clear()
Invalidates all slots and releases every unique-owned object.
void forEachLive(F &&visit)
Visit every live object with its slot index, in slot order.
Borrowed< T > resolve(Ref ref) noexcept
Resolves a live object as a non-owning observation.
Borrowed< const T > resolve(Ref ref) const noexcept
Const overload of resolve(); see the non-const form for its contract.
eve::Result< RuntimePin< T, Tag > > pin(Ref ref)
Keeps one entry alive until the returned pin is released.
RuntimeObjectRegistry(const RuntimeObjectRegistry &)=delete
eve::Result< Ref > emplace(Owned< T > object)
Transfers one unique-owned object into a fresh registry slot.
void forEachLive(F &&visit) const
Read-only overload: the callback receives const T&.
RuntimeObjectRegistry()
Creates an empty registry with a unique owner-lifetime epoch.
RuntimeObjectRegistry(RuntimeObjectRegistry &&) noexcept=default
Constructs a RuntimeObjectRegistry.
RuntimeObjectRegistry & operator=(const RuntimeObjectRegistry &)=delete
RAII keep-alive for one RuntimeObjectRegistry entry.
RuntimeHandleRef< Tag > Ref
Borrowed< T > borrow() const noexcept
Borrows the pinned object together with its owner epoch.
RuntimePin()=default
Constructs an unbound pin.
~RuntimePin()
Releases the keep-alive; an erased object is destroyed here.
RuntimePin & operator=(RuntimePin &&other) noexcept
Move-assigns, releasing any keep-alive this pin already held.
T * get() const noexcept
Borrows the pinned object; valid until the pin is released.
T * operator->() const noexcept
Pointer-like access to the pinned object.
RuntimePin(RuntimePin &&other) noexcept
Moves the keep-alive to a new pin; the source becomes unbound.
T & operator*() const noexcept
Dereferences the pinned object.
RuntimePin(const RuntimePin &)=delete
RuntimePin & operator=(const RuntimePin &)=delete
bool isBound() const noexcept
Whether this pin still keeps an object alive.
const Ref & reference() const noexcept
The reference this pin was created from.
Tag-free slot store behind every RuntimeObjectRegistry<T, Tag>.
RuntimeSlotStore & operator=(RuntimeSlotStore &&)=delete
Result< void > erase(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch)
Destroys the object in a live slot, then advances or retires it.
void forEachLive(F &&visit)
Visits every live slot with its index, in slot order.
void * resolve(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch) const noexcept
Borrows the live object for these coordinates, or null when stale.
RuntimeSlotStore(RuntimeSlotStore &&)=delete
bool isStale(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch) const noexcept
Whether a non-invalid handle can no longer resolve.
RuntimeSlotStore & operator=(const RuntimeSlotStore &)=delete
Result< void * > pin(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch)
Keeps one live slot's object alive across erase() and clear().
Result< RuntimeSlotCoordinates > emplace(void *object)
Stores one object in a fresh or recycled slot.
~RuntimeSlotStore()
Releases RuntimeSlotStore resources.
void forEachLive(F &&visit) const
Read-only overload: the callback receives const void*.
std::uint64_t ownerEpoch() const noexcept
Returns the non-reusable lifetime epoch of this store.
void unpin(std::uint32_t index, std::uint64_t ownerEpoch) noexcept
Releases one pin, destroying the object when it was the last one on an erased slot.
void clear()
Destroys every live object and invalidates every prior handle.
RuntimeSlotStore(OwnedInstanceDestroy destroy) noexcept
Constructs an empty store that owns no slot.
RuntimeSlotStore(const RuntimeSlotStore &)=delete
void(*)(void *) noexcept OwnedInstanceDestroy
Destroys one owned C++ wrapper without knowing its type.
void ownedInstanceDestroy(void *pointer) noexcept
Type-erased cleanup used when the VM never took the object.
std::unique_ptr< T > Owned
C++ owning storage used by an object factory.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
Handle plus owner-lifetime epoch for a non-ECS registry object.
RuntimeHandle< Tag > handle
friend constexpr bool operator==(const RuntimeHandleRef &, const RuntimeHandleRef &) noexcept=default
constexpr bool isValid() const noexcept
Returns whether both the handle and its registry epoch are set.
constexpr std::uint64_t packed() const noexcept
Returns the packed slot/generation projection.
Slot coordinates without the domain tag.