Skip to content

Commit 80b9f2b

Browse files
committed
Refactor advance_proof timed path into _execute_step helper
Extract the submit/result(timeout)/interrupt/shrink_step logic out of the advance_proof loop into a private _execute_step helper, so the loop body reads uniformly: one call plus two except clauses. The helper signals the two timeout outcomes with distinct exceptions -- _StepShrunk (retry the node smaller) and _StepExhausted (cannot shrink, stop) -- rather than a bare None, since control flow (break-and-retry vs return-and-stop) cannot cross the function boundary. No behavior change; the timeout/shrink/retry policy stays in base advance_proof. Addresses review feedback on PR #4930.
1 parent 11cb8ad commit 80b9f2b

1 file changed

Lines changed: 55 additions & 27 deletions

File tree

pyk/src/pyk/proof/proof.py

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,24 @@ def step() -> Iterable[SR]:
461461
return step
462462

463463

464+
class _StepTimedOut(Exception):
465+
"""A timed `step_proof` exhausted its `step_timeout` budget. Carries the budget for logging."""
466+
467+
budget: int
468+
469+
def __init__(self, budget: int) -> None:
470+
super().__init__(budget)
471+
self.budget = budget
472+
473+
474+
class _StepShrunk(_StepTimedOut):
475+
"""The timed-out step was shrunk; `advance_proof` should re-fetch steps and retry it smaller."""
476+
477+
478+
class _StepExhausted(_StepTimedOut):
479+
"""The timed-out step could not be shrunk further; `advance_proof` should stop."""
480+
481+
464482
class Prover(ContextManager['Prover'], Generic[P, PS, SR]):
465483
"""Abstract class which advances `Proof`s with `init_proof()` and `step_proof()`.
466484
@@ -528,6 +546,27 @@ def interrupt(self) -> None:
528546
"""
529547
...
530548

549+
def _execute_step(self, step: PS, executor: Executor | None) -> Iterable[SR]:
550+
"""Run one `step_proof`, honoring `step_timeout` when an `executor` is given.
551+
552+
With no executor, runs the step synchronously with no time limit. With one, runs the step
553+
under the `step_timeout` budget; on timeout it interrupts the step, asks the prover to
554+
`shrink_step`, and raises `_StepShrunk` (a smaller step is ready to retry) or `_StepExhausted`
555+
(already minimal, give up). Returns the step results when the step completes within budget.
556+
"""
557+
if executor is None:
558+
return self.step_proof(step)
559+
560+
budget = self.step_timeout
561+
assert budget is not None
562+
future = executor.submit(self.step_proof, step)
563+
try:
564+
return future.result(timeout=budget)
565+
except FuturesTimeoutError:
566+
self.interrupt()
567+
wait([future]) # let the interrupted step unwind before we reuse the prover
568+
raise (_StepShrunk if self.shrink_step() else _StepExhausted)(budget) from None
569+
531570
def advance_proof(
532571
self,
533572
proof: P,
@@ -553,8 +592,9 @@ def advance_proof(
553592
_LOGGER.info(f'Initializing proof: {proof.id}')
554593
self.init_proof(proof)
555594

556-
timed = self.step_timeout is not None
557-
executor = ThreadPoolExecutor(max_workers=1) if timed else None
595+
# When `step_timeout` is set, run each step on a worker thread so it can be timed and
596+
# interrupted; otherwise steps run synchronously and `executor` stays `None`.
597+
executor = ThreadPoolExecutor(max_workers=1) if self.step_timeout is not None else None
558598
try:
559599
while True:
560600
steps = list(proof.get_steps())
@@ -569,31 +609,19 @@ def advance_proof(
569609
return
570610
if max_iterations is not None and max_iterations <= iterations:
571611
return
572-
if timed:
573-
assert executor is not None
574-
budget = self.step_timeout
575-
assert budget is not None
576-
future = executor.submit(self.step_proof, step)
577-
try:
578-
results = future.result(timeout=budget)
579-
except FuturesTimeoutError:
580-
# The step exhausted its budget: interrupt it, ask the prover to do less
581-
# work per step, and re-fetch steps so the same node is retried smaller.
582-
self.interrupt()
583-
wait([future])
584-
if not self.shrink_step():
585-
_LOGGER.warning(
586-
f'Proof {proof.id}: step exhausted {budget}s budget and cannot be '
587-
f'shrunk further; stopping.'
588-
)
589-
return
590-
_LOGGER.warning(
591-
f'Proof {proof.id}: step exhausted {budget}s budget; shrinking and retrying.'
592-
)
593-
shrank_step = True
594-
break
595-
else:
596-
results = self.step_proof(step)
612+
try:
613+
results = self._execute_step(step, executor)
614+
except _StepShrunk as timeout:
615+
_LOGGER.warning(
616+
f'Proof {proof.id}: step exceeded {timeout.budget}s budget; shrinking and retrying.'
617+
)
618+
shrank_step = True # re-fetch steps and retry the same node, now smaller
619+
break
620+
except _StepExhausted as timeout:
621+
_LOGGER.warning(
622+
f'Proof {proof.id}: step exceeded {timeout.budget}s budget and cannot shrink further; stopping.'
623+
)
624+
return
597625
iterations += 1
598626
for result in results:
599627
proof.commit(result)

0 commit comments

Comments
 (0)