boa-dev / boa

Boa is an embeddable and experimental Javascript engine written in Rust. Currently, it has support for some of the language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rust async function stuck indefinitely when called from js

zlindner opened this issue · comments

commented

Describe the bug
Async rust functions called from js seem to hang/get stuck forever when using tokio.

To Reproduce
Spawn tasks in a loop that execute a simple javascript script which calls a single function sleep that is a rust async native function:

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    loop {
        tokio::spawn(async move {
            let mut context = Context::default();

            context
                .register_global_callable(
                    js_string!("sleep"),
                    0,
                    NativeFunction::from_async_fn(do_something_async),
                )
                .unwrap();

            let script = r"
            function test() {
                sleep();
                return true;
            }";

            context.eval(Source::from_bytes(script)).unwrap();
            let result = context.eval(Source::from_bytes("test()")).unwrap();
            println!("{:?}", result);
        });
    }
}

fn do_something_async(
    _this: &JsValue,
    _args: &[JsValue],
    _context: &mut Context,
) -> impl Future<Output = JsResult<JsValue>> {
    async move {
        println!("Start sleeping...");
        sleep(Duration::from_secs(1)).await;
        println!("Done!");
        Ok(JsValue::Boolean(true))
    }
}

This results in the below output: (this is all the output I get, all 8 "Start sleeping..." lines get printed immediately then gets stuck forever)

Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...
Start sleeping...

Any ideas why this is the case? Not sure if I'm doing something wrong or if this is a bug. It seems to work fine if I either remove the tokio::spawn altogether and just run in the main function, or await the JoinHandle for the tasks.

Build environment (please complete the following information):

  • OS: macOS
  • Version: Tested both 0.17.3 and main branch, same behaviour
  • Rustc version: rustc 1.75.0 (82e1608df 2023-12-21)

Remember that all tokio futures must be polled by a tokio executor to progress. The default job executor doesn't use an async executor, it only blocks on all future jobs.

If you need this functionality, you'd have to override the JobQueue and use a thread-local tokio executor to push all futures forward.

commented

Ok makes sense, thanks for the help :). Will try it out, if I get something working will create a PR adding an example.

Good! Also, remember to call run_jobs_async to push the futures forward, or else the context will just evaluate the code without evaluating any of the futures and promises

Will close this since the original question was resolved.