neon-bindings / neon

Rust bindings for writing safe and fast native Node.js modules.
https://www.neon-bindings.com/
Apache License 2.0
7.98k stars 282 forks source link

Support `async fn` with `#[neon::export]` #1032

Open kjvalencik opened 5 months ago

kjvalencik commented 5 months ago

https://github.com/neon-bindings/neon/pull/1025 introduced #[neon::export]. When the task attribute is included, the body of the function is executed on the libuv worker pool and a JsPromise is returned.

The #[neon::export] macro should also return a JsPromise when the function is async. The transformation is fairly straightforward, but we need a way to spawn a future. Ideally this is done in a runtime agnostic way.

We will need good docs for registering the runtime. Since there are a couple of gotchas:

The macro should catch some common pitfalls and give clear error messages:

Notes

Runtime will likely be registered as instance data. However, users will almost always want to share a runtime across workers. Docs should encourage creating a global static runtime and sharing that across instances.

use std::{io, sync::OnceLock};

use neon::prelude::*;
use tokio::runtime::Runtime;

#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    static RUNTIME: OnceLock<Runtime> = OnceLock::new();

    let runtime = RUNTIME
        .get_or_try_init(|| Runtime::new())
        .or_else(|err| cx.throw_error(err.to_string()))?;

    neon::register_runtime(runtime)?;

    Ok(())
}

This can be simplified by using a macro attribute and defaults.

#[neon::main(tokio)]
fn main(x: ModuleContext) -> Neonresult<()> {
    Ok(())
}

Alternatively, to avoid requiring a #[neon::main] just to register, a feature flag could be provided that automatically creates it.

[dependencies]
neon = { version = "1.1", features = ["tokio-multi-thread"] }