Multi-Threaded Synchronization
Example
use rhai::{Engine};
fn main() {
    // Channel: Script -> Master
    let (tx_script, rx_master) = std::sync::mpsc::channel();
    // Channel: Master -> Script
    let (tx_master, rx_script) = std::sync::mpsc::channel();
    // Spawn thread with Engine
    std::thread::spawn(move || {
        // Create Engine
        let mut engine = Engine::new();
        // Register API
        // Notice that the API functions are blocking
        engine.register_fn("get", move || rx_script.recv().unwrap())
              .register_fn("put", move |v: i64| tx_script.send(v).unwrap());
        // Run script
        engine.run(
        r#"
            print("Starting script loop...");
            loop {
                // The following call blocks until there is data
                // in the channel
                let x = get();
                print(`Script Read: ${x}`);
                x += 1;
                print(`Script Write: ${x}`);
                // The following call blocks until the data
                // is successfully sent to the channel
                put(x);
            }
        "#).unwrap();
    });
    // This is the main processing thread
    println!("Starting main loop...");
    let mut value = 0_i64;
    while value < 10 {
        println!("Value: {value}");
        // Send value to script
        tx_master.send(value).unwrap();
        // Receive value from script
        value = rx_master.recv().unwrap();
    }
}Considerations for sync
std::mpsc::Sender and std::mpsc::Receiver are not Sync, therefore they cannot be used in
registered functions if the sync feature is enabled.
In that situation, it is possible to wrap the Sender and Receiver each in a Mutex or RwLock,
which makes them Sync.
This, however, incurs the additional overhead of locking and unlocking the Mutex or RwLock
during every function call, which is technically not necessary because there are no other references
to them.
The example above highlights the fact that Rhai scripts can call any Rust function, including ones that are blocking.
However, Rhai is essentially a blocking, single-threaded engine. Therefore it does not provide an async API.
That means, although it is simple to use Rhai within a multi-threading environment where blocking a
thread is acceptable or even expected, it is currently not possible to call async functions within
Rhai scripts because there is no mechanism in Engine to wrap the state of the call stack inside
a future.
Fortunately an Engine is re-entrant so it can be shared among many async tasks. It is usually
possible to split a script into multiple parts to avoid having to call async functions.
Creating an Engine is also relatively cheap (extremely cheap if creating a raw Engine),
so it is also a valid pattern to spawn a new Engine instance for each task.