载入中...
搜索中...
未找到
RuntimeRegistry.cpp
浏览该文件的文档.
1
11
12#include <exception>
13#include <limits>
14#include <optional>
15#include <string>
16#include <utility>
17#include <vector>
18
20
21namespace {
22
24template <class R>
25[[nodiscard]] Result<R> registryFailure(DiagnosticCode code, std::string message) {
26 return Result<R>::failure(Diagnostic::error(code, message, {}, {}, "runtime.registry"));
27}
28
30template <class R>
31[[nodiscard]] Result<R> registryStale() {
32 return registryFailure<R>(DiagnosticCode::StaleHandle, "runtime handle is stale or belongs to another owner epoch");
33}
34
35} // namespace
36
38 for (auto& slot : slots_) destroySlot(slot);
39}
40
41bool RuntimeSlotStore::coordinatesValid(std::uint32_t index, std::uint32_t generation) noexcept {
42 return index != std::numeric_limits<std::uint32_t>::max() && generation != std::uint32_t{0};
43}
44
45std::optional<std::uint32_t> RuntimeSlotStore::nextGeneration(std::uint32_t current) noexcept {
46 if (current == std::numeric_limits<std::uint32_t>::max()) return std::nullopt;
47 return static_cast<std::uint32_t>(current + 1u);
48}
49
50std::uint64_t RuntimeSlotStore::nextEpoch() noexcept {
51 std::uint64_t value = nextEpoch_.fetch_add(1, std::memory_order_relaxed);
52 if (value == 0) value = nextEpoch_.fetch_add(1, std::memory_order_relaxed);
53 return value;
54}
55
56std::optional<std::uint32_t> RuntimeSlotStore::findSlot(std::uint32_t index, std::uint32_t generation,
57 std::uint64_t ownerEpoch) const noexcept {
58 if (ownerEpoch != ownerEpoch_ || !coordinatesValid(index, generation) || index >= slots_.size())
59 return std::nullopt;
60 return index;
61}
62
63void RuntimeSlotStore::destroySlot(Slot& slot) noexcept {
64 void* object = slot.object;
65 if (object == nullptr) return;
66 // Clear the slot before running the destructor. A payload may itself hold pins into
67 // this store (resource dependencies do exactly that), and every pin release re-enters
68 // unpin(); observing the object as already gone keeps that re-entry from resolving or
69 // destroying a payload whose destructor is still on the stack.
70 slot.object = nullptr;
71 destroy_(object);
72}
73
75 try {
76 std::uint32_t index = 0;
77 bool appended = false;
78 if (!freeSlots_.empty()) {
79 index = freeSlots_.back();
80 } else {
81 if (slots_.size() >= static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max())) {
82 return registryFailure<RuntimeSlotCoordinates>(DiagnosticCode::Failed,
83 "runtime registry exhausted its slot index space");
84 }
85 index = static_cast<std::uint32_t>(slots_.size());
86 slots_.emplace_back();
87 appended = true;
88 }
89
90 Slot& slot = slots_[index];
91 if (slot.retired || slot.object != nullptr) {
92 if (appended) slots_.pop_back();
93 return registryFailure<RuntimeSlotCoordinates>(DiagnosticCode::InvariantViolation,
94 "runtime registry selected an occupied slot");
95 }
96 const RuntimeSlotCoordinates coordinates{index, slot.generation};
97 slot.object = object;
98 if (!freeSlots_.empty() && freeSlots_.back() == index) freeSlots_.pop_back();
100 } catch (const std::exception& error) {
101 return registryFailure<RuntimeSlotCoordinates>(
102 DiagnosticCode::Failed, std::string("runtime registry allocation failed: ") + error.what());
103 } catch (...) {
104 return registryFailure<RuntimeSlotCoordinates>(DiagnosticCode::Failed, "runtime registry allocation failed");
105 }
106}
107
108void* RuntimeSlotStore::resolve(std::uint32_t index, std::uint32_t generation,
109 std::uint64_t ownerEpoch) const noexcept {
110 const auto slotIndex = findSlot(index, generation, ownerEpoch);
111 if (!slotIndex) return nullptr;
112 const Slot& slot = slots_[*slotIndex];
113 return (slot.object != nullptr && slot.generation == generation) ? slot.object : nullptr;
114}
115
116Result<void*> RuntimeSlotStore::pin(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch) {
117 if (!coordinatesValid(index, generation)) {
119 DiagnosticCode::InvalidArgument, "cannot pin an invalid runtime handle", {}, {}, "runtime.registry"));
120 }
121 if (ownerEpoch != ownerEpoch_) return registryStale<void*>();
122 const auto slotIndex = findSlot(index, generation, ownerEpoch);
123 if (!slotIndex) return registryStale<void*>();
124 Slot& slot = slots_[*slotIndex];
125 if (slot.object == nullptr || slot.generation != generation || slot.orphaned) return registryStale<void*>();
126 if (slot.pins == std::numeric_limits<std::uint32_t>::max())
127 return registryFailure<void*>(DiagnosticCode::InvariantViolation, "runtime registry pin count exhausted");
128 ++slot.pins;
129 return Result<void*>::success(slot.object);
130}
131
132void RuntimeSlotStore::unpin(std::uint32_t index, std::uint64_t ownerEpoch) noexcept {
133 if (ownerEpoch != ownerEpoch_ || index >= slots_.size()) return;
134 Slot& slot = slots_[index];
135 if (slot.pins == 0) return;
136 --slot.pins;
137 if (slot.pins != 0 || !slot.orphaned) return;
138 destroySlot(slot);
139 slot.orphaned = false;
140 if (!slot.retired) {
141 try {
142 freeSlots_.push_back(index);
143 } catch (...) {
144 // The object is already destroyed. Losing the free-list entry only
145 // wastes one slot index; it can never alias a live object.
146 }
147 }
148}
149
150Result<void> RuntimeSlotStore::erase(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch) {
151 if (!coordinatesValid(index, generation)) {
153 DiagnosticCode::InvalidArgument, "cannot erase an invalid runtime handle", {}, {}, "runtime.registry"));
154 }
155 if (ownerEpoch != ownerEpoch_) return registryStale<void>();
156 const auto slotIndex = findSlot(index, generation, ownerEpoch);
157 if (!slotIndex) return registryStale<void>();
158 Slot& slot = slots_[*slotIndex];
159 if (slot.object == nullptr || slot.generation != generation) return registryStale<void>();
160
161 const auto next = nextGeneration(slot.generation);
162 if (slot.pins != 0) {
163 // Outstanding pins keep the object alive, so the slot must not be handed
164 // out again; the handle itself is stale as soon as the generation moves.
165 slot.orphaned = true;
166 if (next)
167 slot.generation = *next;
168 else
169 slot.retired = true;
171 }
172 if (next) {
173 try {
174 freeSlots_.push_back(index);
175 } catch (const std::exception& error) {
176 return registryFailure<void>(DiagnosticCode::Failed,
177 std::string("runtime registry release bookkeeping failed: ") + error.what());
178 } catch (...) {
179 return registryFailure<void>(DiagnosticCode::Failed, "runtime registry release bookkeeping failed");
180 }
181 }
182 destroySlot(slot);
183 if (next)
184 slot.generation = *next;
185 else
186 slot.retired = true;
188}
189
190bool RuntimeSlotStore::isStale(std::uint32_t index, std::uint32_t generation, std::uint64_t ownerEpoch) const noexcept {
191 if (!coordinatesValid(index, generation)) return false;
192 if (ownerEpoch != ownerEpoch_) return true;
193 const auto slotIndex = findSlot(index, generation, ownerEpoch);
194 if (!slotIndex) return true;
195 const Slot& slot = slots_[*slotIndex];
196 return slot.object == nullptr || slot.generation != generation;
197}
198
200 // Reserve before mutating live slots so free-list bookkeeping cannot fail
201 // after ownership has already been released.
202 freeSlots_.reserve(slots_.size());
203 freeSlots_.clear();
204 for (std::uint32_t index = 0; index < slots_.size(); ++index) {
205 Slot& slot = slots_[index];
206 const auto next = nextGeneration(slot.generation);
207 if (slot.pins != 0) {
208 // Objects kept alive by a pin survive the clear; their slots stay out
209 // of the free list until unpin() destroys them.
210 slot.orphaned = true;
211 if (next)
212 slot.generation = *next;
213 else
214 slot.retired = true;
215 continue;
216 }
217 destroySlot(slot);
218 if (next) {
219 slot.generation = *next;
220 freeSlots_.push_back(index);
221 } else {
222 slot.retired = true;
223 }
224 }
225}
226
227} // namespace eve::script::detail
double value
std::string message
DiagnosticCode code
std::string error
Definition Package.cpp:60
std::uint32_t generation
Tag-free slot registry: handles, borrows, unique ownership and keep-alive pins.
double current
Json object
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
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
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 * 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.
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.
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.
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.
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Slot coordinates without the domain tag.