70 static_assert(Policy == WritePolicy::ReadOnly ||
71 Policy == WritePolicy::UniquePerChunk,
72 "auto-exec supports the parallel-phase write policies only");
75 using ResultHook = void (*)(
void* ctx,
OpHandle handle,
77 const Ack* ack)
noexcept;
80 : world_(&world), ops_(&ops), fn_(
static_cast<ChunkFn&&
>(fn)) {}
82 void reserve_operations(std::size_t count) {
83 channel_.reserve_operations(count);
84 scratch_.reserve_operations(count);
89 void use_pool(WorkerPoolPhaseExecutor& pool,
90 std::size_t threshold = 2)
noexcept {
92 parallel_threshold_ = threshold == 0 ? 1 : threshold;
95 void set_result_hook(
void* ctx, ResultHook hook)
noexcept {
110#if TESS_HAS_EXCEPTIONS
112 return run_nonempty();
121 return run_nonempty();
129 for (
const auto& operation : ops_->operations()) {
130 if (operation.write_policy != Policy) {
131 TESS_ASSERT_MSG(
false,
132 "auto-exec queue contains a mismatched write policy");
133 last_run_.status = AutoExecStatus::PolicyMismatch;
140 const auto& report = plan_operations(*world_, *ops_, plan_report_);
141 (void)record_plan_completions(report, channel_);
142 last_run_.planned_ops = report.planned_count();
143 last_run_.rejected_ops = report.failed_count();
146 if (!report.plan().empty()) {
147 const auto phases = plan_parallel_execution_phases(report.plan());
150 TESS_ASSERT(phases.ok());
151 last_run_.phases = phases.phases().size();
152 for (
const auto& phase : phases.phases()) {
153 const auto use_pool =
154 pool_ !=
nullptr && phase.operation_count() >= parallel_threshold_;
156#if TESS_HAS_EXCEPTIONS
160 ++last_run_.pool_phases;
161 result = execute_phase_partitioned_dirty_with_results<Policy>(
162 *pool_, *world_, report.plan(), phase, scratch_, channel_,
163 [
this](
auto view, Ack& ack)
noexcept(
164 std::is_nothrow_invocable_v<ChunkFn&,
decltype(view)&,
165 Ack&>) { fn_(view, ack); });
168 result = execute_phase_partitioned_dirty_with_results<Policy>(
169 serial, *world_, report.plan(), phase, scratch_, channel_,
170 [
this](
auto view, Ack& ack)
noexcept(
171 std::is_nothrow_invocable_v<ChunkFn&,
decltype(view)&,
172 Ack&>) { fn_(view, ack); });
174#if TESS_HAS_EXCEPTIONS
181 detail::merge_planned_dirty_after_exception(*world_, scratch_);
182 TESS_ASSERT(merged.status == PlannedDirtyMergeStatus::Merged);
183 last_run_.merged_dirty_chunks += merged.merged_chunk_count;
187 TESS_ASSERT(result.status == PlannedExecutionStatus::Executed);
188 last_run_.executed_chunks += result.chunk_count;
193#if TESS_HAS_EXCEPTIONS
196 merged = merge_planned_dirty(*world_, scratch_);
197#if TESS_HAS_EXCEPTIONS
202 const auto fallback =
203 detail::merge_planned_dirty_after_exception(*world_, scratch_);
204 TESS_ASSERT(fallback.status == PlannedDirtyMergeStatus::Merged);
205 last_run_.merged_dirty_chunks += fallback.merged_chunk_count;
209 if (merged.status == PlannedDirtyMergeStatus::CapacityExceeded) {
214 detail::merge_planned_dirty_after_exception(*world_, scratch_);
216 detail::fail_fast(
"AutoExec dirty fallback validation failed");
219 TESS_ASSERT(merged.status == PlannedDirtyMergeStatus::Merged);
220 last_run_.merged_dirty_chunks += merged.merged_chunk_count;
222 for (
const auto& operation : report.plan().operations()) {
223 produced_dirty |= operation.field_access.dirty_mask;
225 last_run_.status = AutoExecStatus::Executed;
227 last_run_.status = report.operations().empty() ? AutoExecStatus::Idle
228 : AutoExecStatus::Executed;
235 if (hook_ !=
nullptr) {
236 last_run_.drained += channel_.drain_results(
238 const Ack* ack)
noexcept {
239 hook_(hook_ctx_, handle, completion, ack);
249 WorkerPoolPhaseExecutor* pool_ =
nullptr;
250 std::size_t parallel_threshold_ = 2;
253 ResultHook hook_ =
nullptr;
254 void* hook_ctx_ =
nullptr;
Supplies a task with the current tick, trigger bits, and work allowance.
Definition schedule.h:122