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 + Synchandle - Cancellation — long-running Lua code can be interrupted via
CancelTokenusing a Lua debug hook - Sync API — blocking
Islehandle withTaskfor non-blocking usage - Async API (optional,
tokiofeature) —AsyncIslehandle with Handle/Driver separation, bounded channel backpressure, andAsyncTask<T>which implementsFuture - Coroutine execution (optional,
tokiofeature) — cooperative multitasking viacoroutine_eval/coroutine_call. Multiple Lua coroutines share the same VM and yield when awaiting async Rust functions - Connection pool (optional,
poolfeature) —IslePoolmanages multipleIsleinstances with checkout/return semantics. SupportsCold(fresh VM) andWarm(reuse) strategies - Async connection pool (optional,
pool+tokiofeatures) —AsyncIslePoolis the async counterpart ofIslePool:checkout/try_checkout/checkout_timeoutare async, idle wait usestokio::sync::Notify, and each slot owns both anAsyncIslehandle and itsAsyncIsleDriverso VMs can be joined on shutdown - Zero unsafe in user code — both
IsleandAsyncIsleare 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 withoutArc. - Driver (
AsyncIsleDriver) — sole lifecycle owner. Callshutdown().awaitfor clean thread join, or drop to let the channel-close mechanism terminate the thread naturally.
Usage
Add to your Cargo.toml:
[]
= "0.4"
# For async support (includes coroutine execution):
# mlua-isle = { version = "0.4", features = ["tokio"] }
# For connection pool:
# mlua-isle = { version = "0.4", features = ["pool"] }
# Both:
# mlua-isle = { version = "0.4", features = ["tokio", "pool"] }
Sync API
use Isle;
let isle = spawn.unwrap;
let result: String = isle.eval.unwrap;
assert_eq!;
isle.shutdown.unwrap;
Async API
#
# async
Coroutine execution (async)
#
# async
Connection pool
use ;
let pool = new.unwrap;
// isle returned to pool automatically
pool.shutdown;
Cancellation (sync)
use Isle;
use Duration;
use thread;
let isle = spawn.unwrap;
let task = isle.spawn_eval;
sleep;
task.cancel;
let result = task.wait;
assert!; // IsleError::Cancelled
Cancellation (async)
#
# async
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, exclusive) |
isle.call(func, args) |
Call a global Lua function (async, exclusive) |
isle.exec(closure) |
Run a closure on the Lua thread (async, exclusive) |
isle.coroutine_eval(code) |
Evaluate as a cooperative coroutine |
isle.coroutine_call(func, args) |
Call a function as a cooperative coroutine |
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 |
isle.spawn_coroutine_eval(code) |
Coroutine eval, returns AsyncTask |
isle.spawn_coroutine_call(func, args) |
Coroutine call, returns AsyncTask |
driver.shutdown().await |
Graceful shutdown (drains pending coroutines) |
task.cancel() |
Cancel the running task |
task.cancel_token() |
Access the CancelToken for sharing |
Pool (IslePool, pool feature)
| Method | Description |
|---|---|
IslePool::new(factory, config) |
Create a pool with factory closure |
pool.checkout() |
Checkout an Isle (blocks until available) |
pool.try_checkout() |
Non-blocking checkout, returns None at capacity |
pool.checkout_timeout(dur) |
Checkout with timeout |
pool.active() |
Number of currently checked-out Isles |
pool.idle() |
Number of idle Isles |
pool.shutdown() |
Shut down all idle Isles |
pooled.kill() |
Mark Isle for disposal on drop |
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.