TimeConstrained¶
Status: Stable
documented, exercised by the test suite and/or worked examples, with no known limitations recorded.
Description¶
TimeConstrained[expr, t]
evaluates expr, stopping after t seconds.
TimeConstrained[expr, t, failexpr]
returns failexpr if the time constraint is not met.
TimeConstrained[expr, Infinity]
imposes no time constraint.
Notes
TimeConstrained generates an interrupt to abort the evaluation of expr if the evaluation is not completed within the specified time. TimeConstrained evaluates failexpr only if the evaluation is aborted. TimeConstrained returns $Aborted if the evaluation is aborted and no failexpr is specified. TimeConstrained may give different results on different occasions within a single session, for example as a result of different conditions of internal system caches. TimeConstrained takes account only of CPU time spent inside the main Mathilda kernel process; it does not include additional threads or processes.Examples (14)¶
Every input below was run against the current Mathilda build and its output recorded.
Basic examples (4)¶
In[1]:= TimeConstrained[2 + 2, 1, Print["never printed"]; -1]
Out[1]= 4
In[2]:= TimeConstrained[While[True], 0.2, "timed out"]
Out[2]= "timed out"
In[3]:= TimeConstrained[Print["not run"], 0]
Out[3]= $Aborted
In[4]:= TimeConstrained[{TimeConstrained[While[True], 0.1, "inner"], 7}, 5]
Out[4]= {"inner", 7}
Applications (10)¶
A body that finishes inside its budget returns its value unchanged
An over-budget body is aborted, even inside an auto-compiled loop
The three-argument form returns failexpr on timeout
Failexpr is held and evaluated only if the body is aborted
Only the budget is evaluated up front, so it can be any expression
An Infinity budget imposes no limit
In[10]:= TimeConstrained[FactorInteger[2^64 + 1], Infinity]
Out[10]= {{274177, 1}, {67280421310721, 1}}
A non-positive budget aborts at once, without evaluating the body
Nesting clamps: the 30 s inner budget is cut to the outer 0.3 s
In[12]:= {t1, r} = AbsoluteTiming[TimeConstrained[TimeConstrained[While[True], 30, "inner"], 0.3, "outer"]]; {r, t1 < 1}
Out[12]= {"inner", True}
An inner timeout is local; the outer computation carries on
A budget that is not a number is left unevaluated
Implementation notes¶
Algorithm. builtin_time_constrained implements TimeConstrained[expr, t]
and TimeConstrained[expr, t, failexpr] under HoldAll. Only t is evaluated
up front. tc_parse_time accepts Integer, Real, BigInt, Rational, and
Infinity/DirectedInfinity[1]; anything else leaves the call unevaluated.
Infinity evaluates the body unconstrained. A non-positive or NaN budget aborts
immediately, returning failexpr or $Aborted, and a runaway budget is clamped
to 2147483 s.
For a finite budget two layers are armed:
- Async layer (POSIX only). A
SIGPROFhandler (tc_sigprof_handler) and anITIMER_PROFtimer. The timer is read withgetitimerfirst, and the armed value ismin(t, outer remaining), so nesting clamps rather than refunds. - Cooperative wall-clock layer. A deadline
tc_deadline_sec, computed frommth_mono_secondsand min'd with any enclosing deadline. The evaluator checks it each rewrite step throughtc_check_deadline, and heavy C kernels such as CAD poll the non-jumpingtc_deadline_passedand decline cleanly. This layer is the backstop on WSL 1 and the only layer on native Windows.
The body runs in tc_run_guarded, which holds the sigsetjmp landing pad
(TC_SETJMP, plain setjmp on MinGW). The jump is not always immediate. While
a GMP or FLINT allocation is in flight (tc_gmp_alloc_busy), or inside a heavy
method's tc_async_defer_push region, the handler only flags the timeout. The
jump is then taken at the next safe point (tc_alloc_safepoint, or the
per-step check), and only on the main thread, never from a worker. On unwind,
tc_run_guarded resets the Orderless sort memo, drains Block frames, and
restores the async-defer count, the Quiet and Solve::ifun suppression depths,
and every counter registered with tc_register_unwind_counter. The caller
restores the recursion depth, disarms its timer, and reinstalls the outer timer
charged for the CPU time consumed (at least 1 µs, so an exhausted outer
budget aborts at its next step). It also restores the previous handler, the
jmp_buf and the deadline state. Finally it returns failexpr, evaluated
afterward with a fresh budget, or $Aborted.
Data structures. The state is a process-global tc_jmp_env plus
sig_atomic_t flags (tc_timed_out, tc_gmp_alloc_busy, tc_async_deferred)
and the cooperative deadline pair. Each call saves these on the C stack, so
nested calls compose. The unwind-counter registry is a fixed array of at most 32
int*. GMP and FLINT memory functions are replaced at startup by
tc_install_alloc_guard so that allocator calls are bracketed.
Complexity / limits. The overhead is a few system calls per call plus one
monotonic-clock read per rewrite step while a budget is active. On POSIX the
budget measures CPU time (ITIMER_PROF), not wall-clock time. On Windows it is
cooperative only: a single long C builtin that never returns to the evaluator or
polls the deadline runs to completion. On the timeout path the abandoned
partial tree is leaked, an inherent consequence of siglongjmp. Bounded
allocations made during a deferred jump leak the same way.
HoldAll,Protected.- Returns the value of
exprif it completes withintseconds of CPU time. - Returns
$Abortedif the budget is exhausted and nofailexpris provided. - Returns the (then-evaluated) value of
failexprif the budget is exhausted and the three-argument form is used.failexpris not evaluated when the body completes in time. TimeConstrained[expr, Infinity]imposes no time constraint.- Zero, negative, or non-numeric (NaN) time budgets abort immediately without evaluating
expr; the abort path still produces$Aborted(orfailexprfor the 3-argument form). - The time budget is measured with
ITIMER_PROFand counts only CPU time (user + kernel) consumed by the Mathilda kernel process. Wall-clock time spent in I/O, sleep, or other processes is not charged. - A cooperative wall-clock deadline (
CLOCK_MONOTONIC+ budget) is also armed and checked once per evaluator rewrite step, as a portability backstop on hosts whereITIMER_PROFis unreliable (notably WSL 1, whose syscall-translation layer under-counts CPU time and deliversSIGPROFmany seconds late). On real Linux / macOS theSIGPROFnormally fires first and the cooperative check is a cheap no-op; on broken hosts the cooperative check enforces the deadline between rewrite steps. The only case that escapes both layers is a single long-running C builtin (e.g.FactorIntegeron a huge composite) on a broken host -- it must wait for the lateSIGPROF. - May give different results on different occasions within a single session, for example as a result of different conditions of internal system caches.
- Nested
TimeConstrainedcalls compose: each call saves and restores the previousSIGPROFhandler,ITIMER_PROFstate, and the cooperative-deadline state, so an inner abort does not disturb an outer time budget. - A timeout also restores every C nesting counter a subsystem has registered with
tc_register_unwind_counter(v0.451) — the unwind skips their decrements, and a counter left raised changes behaviour for the rest of the session (one aborted nestedDSolveused to leaveDSolve's recursion depth at 2, disabling its top-level-only methods and its failure memo). - Nesting CLAMPS, it does not refund (v0.253). An inner budget can only ever shorten the enclosing one: the timer actually armed is
min(inner request, outer remaining), and on exit the enclosing budget is reinstalled charged for the CPU time the inner call consumed. SoTimeConstrained[expr, t]is an upper bound on the whole call, however many nested budgetsexprenters. A shorter inner budget still wins, and a computation that fits inside its budget is unaffected. Before this, both layers lifted the caller's deadline merely by entering an inner scope — theITIMER_PROFlayer reinstalled the outer timer at the value it held when the inner call started, and the cooperative layer set an inner absolute deadline that could be later than the outer's — soTimeConstrained[TimeConstrained[<long loop>, 30], 3]ran the loop to completion in 24.5 s. SinceIntegrate,Simplify,NIntegrateandDSolveall bound their own sub-steps this way, no budget was an upper bound in practice. - The abort is implemented by
siglongjmp-ing out of the in-flight evaluator. Expression nodes allocated by the aborted computation are not reclaimed; this is the documented behaviour.
Attributes: HoldAll, Protected.
References¶
See also: HoldAll, FactorInteger, DSolve, Integrate, Simplify, NIntegrate
- Source:
src/core.c - Specification:
docs/spec/builtins/time-and-date.md - Tests:
tests/test_dsolve.c - Tests:
tests/test_dsolve_m34_stress.c - Tests:
tests/test_dsolve_m62_stress.c - Tests:
tests/test_dsolve_m63_stress.c
Notes & additional examples¶
Notes¶
TimeConstrained[expr, t]returns the value ofexprif it finishes withintseconds, and$Abortedotherwise.TimeConstrained[expr, t, failexpr]returnsfailexprinstead, evaluated after the timeout with a fresh budget.- Attributes
HoldAll,Protected. Onlytis evaluated up front. It may be an Integer, a Real, a big integer, a Rational orInfinity. Zero, negative and NaN budgets abort immediately. Budgets are capped near 2147483 s. - On POSIX the budget is CPU time, measured with
ITIMER_PROF/SIGPROF. This interrupts even a long-running C builtin or compiled loop. A cooperative wall-clock deadline is also polled at every evaluator step and by heavy kernels such as CAD, as a backstop on hosts with unreliable profiling timers (WSL 1). Native Windows has only the cooperative layer, so a single long C builtin there runs to completion before the deadline is noticed. - Nesting clamps and never refunds (v0.253). An inner budget is
min(inner, outer remaining), and its CPU time is charged to the outer budget, soTimeConstrained[expr, t]bounds the whole call however many budgetsIntegrate,Simplify,DSolveorNIntegrateopen inside it. When the clamped inner budget is what fires, the innerfailexpris the value you see. - After a timeout the kernel restores the evaluator recursion depth,
Blockframes, message-suppression depth and every C counter registered withtc_register_unwind_counter(v0.451). An aborted call therefore cannot disable later behaviour in the session. - The abort unwinds with
siglongjmp, so expression nodes built by the abandoned computation are leaked by design. Results may differ between runs because the cut-off point depends on load and on cache state. - See also:
AbsoluteTiming,Timing,Check,Quiet.