89 lines
2.6 KiB
Markdown
89 lines
2.6 KiB
Markdown
Maximum Call Stack Depth
|
|
========================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
In Rhai, it is trivial for a function call to perform _infinite recursion_ (or a very deeply-nested
|
|
recursion) such that all stack space is exhausted.
|
|
|
|
```rust
|
|
// This is a function that, when called, recurses forever.
|
|
fn recurse_forever() {
|
|
recurse_forever();
|
|
}
|
|
```
|
|
|
|
```admonish info.side "Main stack size"
|
|
|
|
The main stack-size of a program is _not_ determined by Rust but is platform-dependent.
|
|
|
|
See [this on-line Rust docs](https://doc.rust-lang.org/std/thread/#stack-size) for more details.
|
|
```
|
|
|
|
Because of its intended embedded usage, Rhai, by default, limits function calls to a maximum depth
|
|
of 64 levels (8 levels in debug build) in order to fit into most platforms' default stack sizes.
|
|
|
|
This limit may be changed via the [`Engine::set_max_call_levels`][options] method.
|
|
|
|
A script exceeding the maximum call stack depth will terminate with an error result.
|
|
|
|
This check can be disabled via the [`unchecked`] feature for higher performance (but higher risks as well).
|
|
|
|
```rust
|
|
let mut engine = Engine::new();
|
|
|
|
engine.set_max_call_levels(10); // allow only up to 10 levels of function calls
|
|
|
|
engine.set_max_call_levels(0); // allow no function calls at all (max depth = zero)
|
|
```
|
|
|
|
|
|
```admonish info.small "Additional considerations"
|
|
|
|
When setting this limit, care must be also be taken to the evaluation depth of each _statement_
|
|
within a function.
|
|
|
|
It is entirely possible for a malicious script to embed a recursive call deep inside a nested
|
|
expression or statements block (see [maximum statement depth]).
|
|
|
|
~~~rust
|
|
fn bad_function(n) {
|
|
// Bail out long before reaching the limit
|
|
if n > 10 {
|
|
return;
|
|
}
|
|
|
|
// Nest many, many levels deep...
|
|
if check_1() {
|
|
if check_2() {
|
|
if check_3() {
|
|
if check_4() {
|
|
:
|
|
if check_n() {
|
|
bad_function(n+1); // <- recursive call!
|
|
}
|
|
:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The function call below may still overflow the stack!
|
|
bad_function(0);
|
|
~~~
|
|
```
|
|
|
|
```admonish tip.small "Tip: Getting around the stack size limit"
|
|
|
|
While the stack size of a program's _main_ thread is platform-specific, Rust defaults to a stack
|
|
size of 2MB for spawned threads.
|
|
|
|
This default can further be changed such that a spawned thread has as large a stack as needed.
|
|
|
|
See [the on-line Rust docs](https://doc.rust-lang.org/std/thread/#stack-size) for more details.
|
|
|
|
Therefore, in order to relax the stack size limit for scripts, run the [`Engine`] in a separate
|
|
spawned thread with a larger stack.
|
|
```
|