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 closures 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

  • Rhai’s AST structure is sharable – meaning that one copy of the AST can be run on multiple instances of Engine simultaneously.

  • Rhai’s packages and modules are also sharable.

  • This means that Engine instances can be decoupled from the base system (packages and modules) as well as the scripts (AST) so they can be created very cheaply.

Procedure

Examples

use rhai::def_package;
use rhai::packages::{Package, StandardPackage};

// Define the custom package 'MyCustomPackage'.
//
// Aggregate other base packages simply by listing them after the colon.
def_package! {
    /// My own personal super-duper custom package
    pub MyCustomPackage(module) : StandardPackage {
      // Register additional Rust functions using 'Module::set_native_fn'.
      let hash = module.set_native_fn("foo", |s: ImmutableString| {
          Ok(foo(s.into_owned()))
      });

      // Remember to update the parameter names/types and return type metadata
      // when using the 'metadata' feature.
      // 'Module::set_native_fn' by default does not set function metadata.
      module.update_fn_metadata(hash, &["s: ImmutableString", "i64"]);
  }
}

let ast = /* ... some AST ... */;

let custom_pkg = MyCustomPackage::new();

// The following loop creates 10,000 Engine instances!

for x in 0..10_000 {
    // Create a raw Engine - extremely cheap
    let mut engine = Engine::new_raw();

    // Register custom package - cheap
    custom_pkg.register_into_engine(&mut engine);

    // Evaluate script
    engine.run_ast(&ast)?;
}