mlua-rs / mlua

High level Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau bindings to Rust with async/await support
Other
1.72k stars 139 forks source link

Limiting script execution time (a question and a bug report) #416

Closed boaz-chen closed 4 months ago

boaz-chen commented 5 months ago

First off, thank you for creating and maintaining this wonderful crate!

I'm trying to limit the time a script executes, and what I found to be potentially the most effective way was to set a hook that will reset the thread if the execution time is too long (I would be more than happy to hear about better ways to achieve that).

While experimenting with that, I hit an internal bug that needed to be reported.

Here's hopefully the shortest repro for it:

[dependencies]
mlua = { version = "0.9.8", features = ["lua54", "vendored"] }
use mlua::*;

fn main() {
    limit_function_execution_time();
}

fn limit_function_execution_time() {
    let lua = Lua::new();
    let run_forever: Function = lua
        .load(
            r#"
            function ()
                while true do
                    x = 10
                end
            end
            "#,
        )
        .eval()
        .unwrap();

    let thread = lua.create_thread(run_forever).unwrap();
    let start = std::time::SystemTime::now();

    thread.set_hook(
        HookTriggers::default().every_nth_instruction(100000),
        move |lua, _debug| {
            let do_nothing: Function = lua
                .load(
                    r#"
                    function ()
                        print("doing nothing")
                    end
                "#,
                )
                .eval()
                .unwrap();

            let now = std::time::SystemTime::now();
            let running_time = now.duration_since(start).unwrap().as_millis();
            println!("running time:{}", running_time);

            if running_time > 100 {
                println!("Thread ran too long. Resetting thread...");
                let thread = lua.current_thread();
                thread.reset(do_nothing).unwrap();
                thread.resume::<(), ()>(()).unwrap();
            }
            Ok(())
        },
    );
    thread.resume::<(), ()>(()).unwrap();
    std::thread::sleep(std::time::Duration::from_secs(30));
}

Output:

mlua internal error: 1 too many stack values popped (this is a bug, please file an issue)

Thanks!

khvzak commented 5 months ago

Thank you for the bug report! It was a bug in mlua and users are not allowed to reset running coroutines (it's not supported by Lua itself).

Answering your question. To stop executing Lua script you can simply return a error from the hook and coroutine will finish immediately with the returned error.

boaz-chen commented 4 months ago

Thanks. This is working perfectly.