Repository navigation
state_exec: a time budget cut inside a step reads as the time limit - #697
Merged
Merged
Conversation
A long loop inside one native call (a data job, a generator body that
never yields, a stdlib materializer, a deep compare) polled the step's
deadline and threw a plain runtime_error ("collections.range: evaluation
exceeded time budget"). So the same time cap read three ways: Ariadne
predicates said "failed at eval (...)" instead of "(time cap)", the async
scheduler's exit error was that message instead of time_limit_exceeded,
and the sync scheduler threw it out of run() instead of killing the
process.
- The polls go through eval_deadline_check(what), which throws
evaluation_timeout ("<what>: evaluation exceeded timeout").
evaluation_timeout and evaluation_interrupted move to types.h, next to
the deadline; evaluator.h still gets them through types.h.
- Both schedulers kill a process whose step throws evaluation_timeout
with time_limit_exceeded when the step's own deadline is spent
(eval_deadline_guard::expired()), as check_limits() does between
steps; that includes the async per-step cap. A deadline armed outside
the sync scheduler still propagates out of run(); other errors are
unchanged.
- The tree-walking evaluator and both async evaluators' sync entry
points arm the thread's deadline for their timeout, as
stackless_evaluator::run does. Before, a loop inside one native call
was never cut there, and the tree-walking evaluator's join() waited
on it for ever.
- Developer guide and pycvc parity doc updated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A process's time cap is checked between steps, and inside a step by the step's deadline. Long loops inside one native call poll that deadline: data jobs, generator bodies that never yield, stdlib materializers and the deep compare. They threw a plain
std::runtime_error("collections.range: evaluation exceeded time budget"), not theevaluation_timeoutthe evaluator throws. So one time cap was reported in two ways, depending on which check saw it first:exceeded the eval budget (time cap)failed at eval (collections.range: evaluation exceeded time budget)time_limit_exceededcollections.range: evaluation exceeded time budgetruntime_errorout ofrun()pycvc.evaluation_timeoutRuntimeErrorThis is what made
AriadneStdlib.PredicateMaterializerIsCutByTheEvalBudgetflaky on slow Debug builds (#680 had to accept both wordings). The user decided to make them match.What
eval_deadline_check(what)throwsevaluation_timeout("<what>: evaluation exceeded timeout"). All six polls use it: three in data_job.cpp, the deep compare, generator.h, and the stdlib'spoll_deadline.evaluation_timeoutandevaluation_interruptedmove from evaluator.h to types.h, next to the deadline. evaluator.h includes types.h, so every user still sees them.evaluation_timeoutwhile the step's own deadline is spent kills the process withtime_limit_exceeded, the reasoncheck_limits()gives between steps.eval_deadline_guard::expired()checks only that guard.max_step_time).run(), and other errors still propagate as before. Ariadne'sinit:lane runs on the sync scheduler, so it now reports "did not complete normally" either way.stackless_evaluator::runalready armed the thread's deadline for its timeout. Now the tree-walkingevaluator(in its worker thread) and thesync_evaluate*entry points ofasync_evaluatorandasync_stackless_evaluatorarm it too.join()then waited on it forever:interrupt()is never seen inside a native loop.eval_deadline_checkand the moved classes.Tests
state_exec_scheduler_testStepDeadlineTest:eval_deadline_checkthrowsevaluation_timeoutnaming the call;(next (generator (lambda () (while t 1))))withevaluation_timeout;time_limit_exceededfor a cut inside a step, between steps, and by the per-step cap, and still reports another error by its message.ariadne_runtime_test:PredicateMaterializerIsCutByTheEvalBudgetexpectsexceeded the eval budgetonly.pycvc.evaluation_timeout;time_limit_exceeded.Local results (MSVC 14.38 Release, deps from the cvcpkg prefix): all 77 state_exec, Ariadne,
async_laneandstate*suites pass (2111 tests, including the 5 newStepDeadlineTestones; the Ariadne predicate test 5 repeat runs clean); pycvc (exec, ari, state, app, async and the three state_exec files) 120 passed; clang-format 18 clean on the changed lines.