Limiting Run Time

Track Progress and Force-Termination

Operations count vs. progress

Operations count does not indicate the proportion of work already done – thus it is not real progress tracking.

The real progress can be estimated based on the expected number of operations in a typical run.

It is impossible to know when, or even whether, a script run will end (a.k.a. the Halting Problem).

When dealing with third-party untrusted scripts that may be malicious, in order to track evaluation progress and force-terminate a script prematurely (for any reason), provide a closure to the Engine via Engine::on_progress.

The closure passed to Engine::on_progress will be called once for every operation.

Progress tracking is disabled with the unchecked feature.

Examples

Periodic Logging

let mut engine = Engine::new();

engine.on_progress(|count| {    // parameter is number of operations already performed
    if count % 1000 == 0 {
        println!("{count}");    // print out a progress log every 1,000 operations
    }
    None                        // return 'None' to continue running the script
                                // return 'Some(token)' to immediately terminate the script
});

Limit running time

let mut engine = Engine::new();

let start = get_time();         // get the current system time

engine.on_progress(move |_| {
    let now = get_time();

    if now.duration_since(start).as_secs() > 60 {
        // Return a dummy token just to force-terminate the script
        // after running for more than 60 seconds!
        Some(Dynamic::UNIT)
    } else {
        // Continue
        None
    }
});

Function Signature of Callback

The signature of the closure to pass to Engine::on_progress is as follows.

Fn(operations: u64) -> Option<Dynamic>

Return value

ValueEffect
Some(token)terminate immediately, with token (a Dynamic value) as termination token
Nonecontinue script evaluation

Termination Token

Token

The termination token is commonly used to provide information on the reason behind the termination decision.

The Dynamic value returned is a termination token.

A script that is manually terminated returns with the error EvalAltResult::ErrorTerminated(token, position) wrapping this value.

If the termination token is not needed, simply return Some(Dynamic::UNIT) to terminate the script run with () as the token.