-
-
Notifications
You must be signed in to change notification settings - Fork 5
Resource Limits
DScript can enforce two independent limits on script execution to prevent runaway scripts from hanging the host process.
engine.SetTimeout(TimeSpan.FromMilliseconds(500));Execution is cancelled if the script runs for longer than the specified wall-clock duration. The check is made every 1024 VM instructions to minimise overhead.
engine.SetInstructionLimit(10_000_000);Execution is cancelled after the VM dispatches the given number of bytecode instructions. This is deterministic — the same script always trips the limit at the same point regardless of CPU speed.
engine.SetTimeout(TimeSpan.FromSeconds(2));
engine.SetInstructionLimit(50_000_000);Both limits are active simultaneously. Execution stops at whichever is reached first.
Both limits throw ScriptTimeoutException, which is a plain .NET exception not derived from JITException or ScriptException. This means script-level try/catch blocks cannot intercept it — it always propagates to the C# host.
try
{
engine.SetTimeout(TimeSpan.FromMilliseconds(100));
engine.Run(ScriptEngine.Compile("while(true) {}"));
}
catch (ScriptTimeoutException ex)
{
Console.Error.WriteLine($"Timed out: {ex.Message}");
}Deeply or infinitely recursive scripts are bounded by MaxCallStackDepth so they
throw a catchable "Maximum call stack size exceeded" error rather than
overflowing the native .NET stack — which is uncatchable and would crash the
process.
engine.MaxCallStackDepth = 5000; // default is 10000Top-level execution runs on a large (256 MB) stack, so the default 10000
sits well below the real overflow point with room for the error to unwind. Tail
calls are trampolined and do not count toward the depth, so tail-recursive
loops are unbounded. The error is reported to the host (e.g. caught by the REPL,
which keeps running); like DScript's other internal errors it is not catchable by
a script-level try/catch.
Call the setters again to update the limits, or pass TimeSpan.Zero / 0 to disable them:
engine.SetTimeout(TimeSpan.Zero); // disable wall-clock limit
engine.SetInstructionLimit(0); // disable instruction limit-
_instrCountis reset to zero at the start of eachRun()call. - The instruction limit check happens on every instruction outside the script's
try/catchdispatcher. - The wall-clock check samples
DateTime.UtcNowonly every 1024 instructions to avoid the cost of a system call on every dispatch.