mlua icon indicating copy to clipboard operation
mlua copied to clipboard

Using mlua with axum

Open CheatCod opened this issue 3 years ago • 5 comments

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!

CheatCod avatar Oct 23 '22 19:10 CheatCod

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

coderedart avatar Oct 24 '22 02:10 coderedart

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

Altair-Bueno avatar Jan 12 '23 21:01 Altair-Bueno

You'd want a Tokio Mutex or other async-friendly Mutex.

khionu avatar Apr 03 '23 19:04 khionu

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;
                    }
                    _ => {}
                }
            }
        });
    });

JavaHello avatar Jan 10 '24 01:01 JavaHello

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.

khionu avatar Jan 13 '24 02:01 khionu