51 OperationStatus status = OperationStatus::Planned;
52 OperationFailure failure = OperationFailure::None;
53 PlannedExecutionStatus execution = PlannedExecutionStatus::Executed;
54 std::size_t chunk_count = 0;
55 std::source_location source = std::source_location::current();
56 bool completed =
false;
58 [[nodiscard]]
constexpr bool ok()
const noexcept {
59 return completed && status == OperationStatus::Planned &&
60 failure == OperationFailure::None &&
61 execution == PlannedExecutionStatus::Executed;
93 static_assert(std::is_default_constructible_v<T>,
94 "result payloads are default-constructed into slots");
97 ResultChannel() =
default;
98 ResultChannel(
const ResultChannel&) =
delete;
99 auto operator=(
const ResultChannel&) -> ResultChannel& =
delete;
100 ResultChannel(ResultChannel&&) =
delete;
101 auto operator=(ResultChannel&&) -> ResultChannel& =
delete;
105 void reserve_operations(std::size_t count) { slots_.reserve(count); }
109 void clear()
noexcept {
114 [[nodiscard]]
auto state(
OpHandle handle)
const noexcept -> OpResultState {
115 if (handle.value >= slots_.size()) {
116 return OpResultState::Unbound;
118 return slots_[
static_cast<std::size_t
>(handle.value)].state;
123 [[nodiscard]]
auto completion(
OpHandle handle)
const noexcept
125 if (handle.value >= slots_.size()) {
128 return slots_[
static_cast<std::size_t
>(handle.value)].completion;
131 [[nodiscard]]
auto size()
const noexcept -> std::size_t {
132 return slots_.size();
137 [[nodiscard]]
auto generation()
const noexcept -> std::uint64_t {
150 template <
typename Visitor>
151 auto drain_results(Visitor&& visit) -> std::size_t {
152 std::size_t visited = 0;
153 const auto drain_generation = generation_;
154 for (std::size_t index = 0;
155 generation_ == drain_generation && index < slots_.size(); ++index) {
156 if (slots_[index].drained ||
157 (slots_[index].state != OpResultState::Ready &&
158 slots_[index].state != OpResultState::Failed)) {
161 const auto has_value = slots_[index].state == OpResultState::Ready;
166 slots_[index].drained =
true;
168 visit(
OpHandle{
static_cast<std::uint64_t
>(index)},
169 slots_[index].completion,
170 has_value ? &slots_[index].value :
nullptr);
172 if (generation_ == drain_generation && index < slots_.size()) {
173 slots_[index].drained =
false;
186 OpResultState state = OpResultState::Unbound;
187 bool drained =
false;
195 if (handle.value >= slots_.size()) {
196 slots_.resize(
static_cast<std::size_t
>(handle.value) + 1);
200 void prepare_operation(
OpHandle handle, std::source_location source) {
202 auto& slot = slots_[
static_cast<std::size_t
>(handle.value)];
205 slot.completion.source = source;
206 slot.state = OpResultState::Pending;
207 slot.drained =
false;
210 [[nodiscard]]
auto value_for(
OpHandle handle)
noexcept -> T& {
211 TESS_ASSERT(handle.value < slots_.size());
212 return slots_[
static_cast<std::size_t
>(handle.value)].value;
216 std::source_location source)
noexcept {
217 TESS_ASSERT(handle.value < slots_.size());
218 auto& slot = slots_[
static_cast<std::size_t
>(handle.value)];
219 slot.completion.status = OperationStatus::Planned;
220 slot.completion.failure = OperationFailure::None;
221 slot.completion.execution = result.status;
222 slot.completion.chunk_count = result.chunk_count;
223 slot.completion.source = source;
224 slot.completion.completed =
true;
225 slot.state = result.status == PlannedExecutionStatus::Executed
226 ? OpResultState::Ready
227 : OpResultState::Failed;
232 auto& slot = slots_[
static_cast<std::size_t
>(handle.value)];
235 slot.completion.status = report.status;
236 slot.completion.failure = report.failure;
237 slot.completion.chunk_count = report.chunk_count;
238 slot.completion.source = report.source;
239 slot.completion.completed =
true;
240 slot.state = OpResultState::Failed;
241 slot.drained =
false;
244 template <
typename U>
246 ResultChannel<U>& channel) -> std::size_t;
248 template <WritePolicy Policy,
typename Executor,
typename World,
typename U,
250 friend auto execute_phase_partitioned_dirty_with_results(
255 template <WritePolicy Policy,
typename World,
typename U,
typename Fn>
256 friend auto execute_plan_deferred_dirty_with_results(
260 std::vector<Slot> slots_;
261 std::uint64_t generation_ = 0;
297 const auto operations = plan.operations();
301 const auto phase_validation =
302 detail::execution_phase_validation_status<Policy>(world, plan, phase);
303 if (phase_validation != PlannedExecutionStatus::Executed) {
304 detail::record_execution_phase_validation_failure(phase_validation);
311 TESS_DIAG_EVENT_VALUE(queued_phase_execute, phase.operation_count());
312 TESS_DIAG_EVENT_VALUE(queued_partitioned_phase, phase.operation_count());
313 for (
const auto& operation :
314 operations.subspan(phase.first_operation(), phase.operation_count())) {
315 channel.prepare_operation(operation.handle, operation.source);
317 scratch.prepare(world, phase.operation_count());
318 auto&& callback = fn;
319 auto result = execute_operation_index_range(
320 std::forward<Executor>(executor), executor_phase_range(phase),
321 [&](std::size_t index) {
322 const auto offset = index - phase.first_operation();
323 const auto& operation = operations[index];
324 auto& value = channel.value_for(operation.handle);
325 auto operation_result =
326 detail::execute_validated_phase_operation_deferred_dirty<Policy>(
327 world, operation, scratch.dirty_for_operation(offset),
328 [&](
auto view) { callback(view, value); });
329 channel.complete(operation.handle, operation_result, operation.source);
330 scratch.record_result(offset, operation_result);
331 return operation_result;
334 std::size_t chunk_count = 0;
335 for (
const auto operation_result : scratch.results()) {
336 if (operation_result.status != PlannedExecutionStatus::Executed) {
337 TESS_DIAG_EVENT(queued_phase_failure);
339 operation_result.status,
343 chunk_count += operation_result.chunk_count;
346 if (result.status != PlannedExecutionStatus::Executed) {
347 TESS_DIAG_EVENT(queued_phase_failure);
355 PlannedExecutionStatus::Executed,
380 world, operation, dirty, [&](
auto view) { callback(view, value); });
friend auto execute_phase_partitioned_dirty_with_results(Executor &&executor, World &world, const ExecutionPlan &plan, const ExecutionPhase &phase, PlannedPhaseExecutionScratch &scratch, ResultChannel< T > &channel, Fn &&fn) -> PlannedExecutionResult
Executes one phase while publishing per-operation payloads and completions.
Definition result_channel.h:293