Using mlua with axum
Since Lua isn't Sync, I am wondering if there's any way to use mlua with axum.
I am trying to have one of the axum handlers receive a piece of lua code and execute it asynchronously in a separate task, but axum handler requires futures to be Sync.
Here is a minimum example:
#[debug_handler]
async fn lua() {
let lua = Lua::new();
tokio::task::spawn(async move {
lua.load("").exec_async().await;
});
}
and the error message:
future cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `std::cell::UnsafeCell<mlua::lua::LuaInner>`
If I use spawn_local, something similar pops up:
#[debug_handler]
async fn lua() {
let lua = Lua::new();
let local = task::LocalSet::new();
local
.run_until(async move {
tokio::task::spawn_local(async move {
lua.load("print('hello')").exec_async().await;
})
.await
.unwrap();
})
.await;
}
future cannot be sent between threads safely
within `impl futures_util::Future<Output = ()>`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<tokio::task::local::Context>`
Any pointers will be highly appreciated!
Did you enable send feature ? That should enable you to move it into closure.
Also, you can wrap lua in arc + mutex to share it between threads
Also, you can wrap lua in arc + mutex to share it between threads
Because MutexGuard doesn't implement Send, it is imposible to hold the lock to Lua interpreter across and await.
// vvv MutexGuard, !Send.
let lua = mutex.lock();
let foo: Function = lua.globals().get("foo")?;
// ^^^ `foo` has a reference to lua
let res = foo.call_async(foo).await?;
// ^^^^^
// await requires that `lua` (the mutex guard) is Send
- Mutexguard !Send: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html#impl-Send-for-MutexGuard%3C%27_%2C%20T%3E
You'd want a Tokio Mutex or other async-friendly Mutex.
You can use std::thread::spawn to execute in a separate thread and tokio::mpsc channels to receive messages to execute lua scripts
let handle = Handle::current();
std::thread::spawn(move || {
let lua = Lua::new();
handle.block_on(async {
while let Some(cmd) = rx.recv().await {
match cmd {
Command::Shutdown => {
info!("lua shutdown");
lua.load("").exec().unwrap();
break;
}
_ => {}
}
}
});
});
No, actually, don't spawn threads when you're using Tokio unless you're tailoring your thread pools for Tokio and normal threads already. Just use spawn_blocking.