mlua-isle 0.3.0

Thread-isolated Lua VM with cancellation and async bridge for mlua
Documentation

mlua-isle

Thread-isolated Lua VM with cancellation and async bridge for mlua.

Problem

mlua::Lua is !Send — it cannot cross thread boundaries. This makes it difficult to use from async runtimes, UI threads, or any multi-threaded context.

mlua-isle solves this by confining the Lua VM to a dedicated thread and communicating via channels.

Features

  • Thread isolation — Lua VM runs on a dedicated thread; callers interact via a Send + Sync handle
  • Cancellation — long-running Lua code can be interrupted via CancelToken using a Lua debug hook
  • Sync API — blocking Isle handle with Task for non-blocking usage
  • Async API (optional, tokio feature) — AsyncIsle handle with Handle/Driver separation, bounded channel backpressure, and AsyncTask<T> which implements Future
  • Zero unsafe in user code — both Isle and AsyncIsle are safe to share across threads

Architecture

Sync (Isle)

┌─────────────────┐  std mpsc  ┌──────────────────┐
│  caller thread   │──────────►│  Lua thread       │
│                  │           │  (mlua confined)   │
│  Isle handle     │◄──────────│                    │
│                  │  oneshot   │  Lua VM + hook    │
└─────────────────┘            └──────────────────┘

Async (AsyncIsle, requires tokio feature)

┌──────────────────┐                ┌──────────────────┐
│  tokio tasks      │  tokio mpsc   │  Lua thread       │
│                   │──────────────►│  (mlua confined)   │
│  AsyncIsle handle │  (bounded,    │                    │
│  (Clone, no Arc)  │  backpressure)│  Lua VM + hook    │
│                   │◄──────────────│                    │
│                   │   oneshot     │                    │
├──────────────────┤                │                    │
│  AsyncIsleDriver  │───done_tx────►│                    │
│  (lifecycle owner)│               └──────────────────┘
└──────────────────┘
  • Handle (AsyncIsle) — lightweight, cloneable. Share across tasks without Arc.
  • Driver (AsyncIsleDriver) — sole lifecycle owner. Call shutdown().await for clean thread join, or drop to let the channel-close mechanism terminate the thread naturally.

Usage

Add to your Cargo.toml:

[dependencies]
mlua-isle = "0.3"

# For async support:
# mlua-isle = { version = "0.3", features = ["tokio"] }

Sync API

use mlua_isle::Isle;

let isle = Isle::spawn(|lua| {
    lua.globals().set("greeting", "hello")?;
    Ok(())
}).unwrap();

let result: String = isle.eval("return greeting").unwrap();
assert_eq!(result, "hello");

isle.shutdown().unwrap();

Async API

# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
use mlua_isle::AsyncIsle;

let (isle, driver) = AsyncIsle::spawn(|lua| {
    lua.globals().set("greeting", "hello")?;
    Ok(())
}).await?;

// Clone freely — no Arc needed.
let isle2 = isle.clone();

let result: String = isle.eval("return greeting").await?;
assert_eq!(result, "hello");

driver.shutdown().await?;
# Ok(())
# }

Cancellation (sync)

use mlua_isle::Isle;
use std::time::Duration;
use std::thread;

let isle = Isle::spawn(|_| Ok(())).unwrap();

let task = isle.spawn_eval("while true do end");

thread::sleep(Duration::from_millis(50));
task.cancel();

let result = task.wait();
assert!(result.is_err()); // IsleError::Cancelled

Cancellation (async)

# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
use mlua_isle::AsyncIsle;
use std::time::Duration;

let (isle, driver) = AsyncIsle::spawn(|_lua| Ok(())).await?;
let task = isle.spawn_eval("while true do end");

let token = task.cancel_token().clone();
tokio::spawn(async move {
    tokio::time::sleep(Duration::from_millis(100)).await;
    token.cancel();
});

let result = task.await; // Err(Cancelled)
assert!(result.is_err());
driver.shutdown().await?;
# Ok(())
# }

API

Sync (Isle)

Method Description
Isle::spawn(init) Create a Lua VM on a dedicated thread
isle.eval(code) Evaluate a Lua chunk (blocking)
isle.call(func, args) Call a global Lua function (blocking)
isle.exec(closure) Run an arbitrary closure on the Lua thread
isle.spawn_eval(code) Non-blocking eval, returns a Task
isle.spawn_call(func, args) Non-blocking call, returns a Task
isle.spawn_exec(closure) Non-blocking exec, returns a Task
isle.shutdown() Graceful shutdown and thread join
task.wait() Block until the task completes
task.cancel() Cancel the running task

Async (AsyncIsle, tokio feature)

Method Description
AsyncIsle::spawn(init) Create a Lua VM, returns (AsyncIsle, AsyncIsleDriver)
AsyncIsle::builder() Configure channel capacity / thread name
isle.eval(code) Evaluate a Lua chunk (async)
isle.call(func, args) Call a global Lua function (async)
isle.exec(closure) Run a closure on the Lua thread (async)
isle.spawn_eval(code) Returns a cancellable AsyncTask
isle.spawn_call(func, args) Returns a cancellable AsyncTask
isle.spawn_exec(closure) Returns a cancellable AsyncTask
driver.shutdown().await Graceful shutdown and thread join
task.cancel() Cancel the running task
task.cancel_token() Access the CancelToken for sharing

Minimum Supported Rust Version

Rust 1.77 or later.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.