One Engine Instance Per Call
Usage Scenario
-
A system where scripts are called a lot, in tight loops or in parallel.
-
Keeping a global
Engine
instance is sub-optimal due to contention and locking. -
Scripts need to be executed independently from each other, perhaps concurrently.
-
Scripts are used to create Rust closure that are stored and may be called at any time, perhaps concurrently. In this case, the
Engine
instance is usually moved into the closure itself.
Key Concepts
-
Create a single instance of each standard package required. To duplicate
Engine::new
, create aStandardPackage
. -
Gather up all common custom functions into a custom package.
-
Store a global
AST
for use with all engines. -
Always use
Engine::new_raw
to create a rawEngine
, instead ofEngine::new
which is much more expensive. A rawEngine
is extremely cheap to create.Registering the
StandardPackage
into a rawEngine
viaEngine::register_global_module
is essentially the same asEngine::new
.However, because packages are shared, using existing package is much cheaper than registering all the functions one by one.
-
Register the required packages with the raw
Engine
viaEngine::register_global_module
, usingPackage::as_shared_module
to obtain a shared module.
Examples
#![allow(unused)] fn main() { use rhai::packages::{Package, StandardPackage}; let ast = /* ... some AST ... */; let std_pkg = StandardPackage::new(); let custom_pkg = MyCustomPackage::new(); let make_call = |x: i64| -> Result<(), Box<EvalAltResult>> { // Create a raw Engine - extremely cheap let mut engine = Engine::new_raw(); // Register packages as global modules - cheap engine.register_global_module(std_pkg.as_shared_module()); engine.register_global_module(custom_pkg.as_shared_module()); // Create custom scope - cheap let mut scope = Scope::new(); // Push variable into scope - relatively cheap scope.push("x", x); // Evaluate script. engine.consume_ast_with_scope(&mut scope, &ast) }; // The following loop creates 10,000 Engine instances! for x in 0..10_000 { make_call(x)?; } }