• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
      • Theories
      • Rule-classes
      • Proof-builder
      • Recursion-and-induction
      • Hons-and-memoization
      • Events
      • Parallelism
      • History
      • Programming
      • Operational-semantics
      • Real
      • Start-here
      • Debugging
      • Miscellaneous
        • Term
        • Ld
        • Hints
        • Type-set
        • Ordinals
        • Clause
        • ACL2-customization
        • With-prover-step-limit
        • Set-prover-step-limit
        • With-prover-time-limit
        • Local-incompatibility
        • Set-case-split-limitations
        • Subversive-recursions
        • Specious-simplification
        • Defsum
        • Gcl
        • Oracle-timelimit
          • Thm
          • Defopener
          • Case-split-limitations
          • Set-gc-strategy
          • Default-defun-mode
          • Top-level
          • Reader
          • Ttags-seen
          • Adviser
          • Ttree
          • Abort-soft
          • Defsums
          • Gc$
          • With-timeout
          • Coi-debug::fail
          • Expander
          • Gc-strategy
          • Coi-debug::assert
          • Sin-cos
          • Def::doc
          • Syntax
          • Subversive-inductions
        • Output-controls
        • Macros
        • Interfacing-tools
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Oracle-time
    • Miscellaneous

    Oracle-timelimit

    Carry out some computation, returning (not just printing!) how long it took and (on supported Lisps) how many bytes were allocated, but aborting the execution if it takes too long.

    The oracle-timelimit macro is similar to oracle-time except that besides reporting times, it also allows you impose a limit on how long the form is allowed to run for and how much memory it is allowed to use. If execution takes too long or allocates too much memory, execution is aborted.

    Warning. This book is intended to be a practically useful tool, but it may not be entirely sound. In particular:

    • In case of a timeout, execution will (of course) not complete normally. If the computation you are timing includes, for instance, updates to stobjs or the state, then the stobjs may have been only partially updated. This might especially pose a soundness problem for abstract stobjs since the stobj invariant might be ruined if a sequence of writes is interrupted.
    • The internal, core, intermediate part of the computation makes use of a return-last style macro that will not return the right results when the computation is timed out. The top-level oracle-timelimit macro accounts for this, but in principle a malicious user could directly call the internal macro to easily cause unsoundness.
    Basic Example
    (oracle-timelimit 1/3 ; Fail if more than 1/3 second is needed
      (fib 35))           ; What to execute
    -->
    (mv successp          ; Did the computation complete in time?
        seconds           ; Time taken (rational number of seconds)
        bytes             ; Bytes allocated (natural)
        ans               ; Answer on success, or NIL on timeout
        state)            ; Adjusted with changes to oracle
    Example with Multiple Return Values
    (oracle-timelimit 5        ; Fail if more than 5 seconds are needed
      (random$ 100 state)      ; What to execute
      :ret    (mv ans state)   ; Return signature of the form to execute
      :onfail (mv :oops state) ; Alternate values to return on timeout
      )
    -->
    (mv successp          ; Did the computation complete in time?
        seconds           ; Time taken (rational number of seconds)
        bytes             ; Bytes allocated (natural)
        ans               ; Answer on success, or :oops on timeout
        state)            ; Adjusted with changes to the oracle

    See also the community book tools/oracle-timelimit-tests.lisp for some additional tests and working examples.

    General Form
    (oracle-timelimit
      timelimit            ; rational valued time limit
      form                 ; what to execute
      [:ret    retspec]    ; return signature for form
      [:onfail failspec]   ; return values for timeout case
      [:maxmem bytes]      ; maximum memory allocation to allow (CCL Only)
    
      ;; Special option to catch Lisp errors that arise during form
      [:suppress-lisp-errors bool]
      )

    The timelimit should evaluate to a rational number which is interpreted as some number of seconds.

    The form can be any arbitrary ACL2 form that you want to execute. If this form returns an ordinary, single value, e.g., as in (fib 35), then the :ret form is not needed. Otherwise, :ret should explain the return signature of form, as in oracle-time.

    (CCL Only) If provided, the :maxmem should specify a memory ceiling in bytes; the default is nil, in which case no memory ceiling is imposed. This is a very rough mechanism and its implementation may change in the future. The memory usage is checked only occasionally (i.e., once per second or similar). Note that we check the global memory usage of the entire ACL2 process, with no regard to which thread has allocated the memory or how much memory was allocated before the computation began.

    The return value of oracle-timelimit extends the return value of form with multiple values. The basic idea is that oracle-timelimit is going to macroexpand to something like the following:

    (mv-let retspec
        form
      (b* (((mv & successp state) (read-acl2-oracle state))
           ((mv & time state)     (read-acl2-oracle state))
           ((mv & bytes state)    (read-acl2-oracle state))
           ;; Fix time/bytes to sensible values
           (time     (if (and (rationalp time) (<= 0 time)) time 0))
           (bytes    (nfix bytes))
           ((unless successp)
            ;; execution timed out
            (mv nil time bytes failspec [state])))
         ;; Else, execution succeeded
         (mv t time bytes retspec [state])))

    You can see here that the retspec is used to explain how to bind the results of executing the form. It is also, essentially, spliced into the return values for the success and failure cases. The only twist is that if retspec mentions state, then we don't add an extra state onto the end of the form.

    By default, if form causes a raw Lisp error such as a type error, stack overflow, or causes some other non-local exit such as throwing to a tag, the error will propagate through the oracle-timelimit call. However, if you set :suppress-lisp-errors t, then any such error will be treated as a timeout. This may have any number of unsound consequences!