feat(napi): introduce AsyncBlock to let user dispose resources after future done (#2338)

This commit is contained in:
LongYinan 2024-10-31 09:24:30 +08:00 committed by GitHub
parent 00a3c13b42
commit f705454029
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 130 additions and 47 deletions

View File

@ -410,6 +410,13 @@ pub fn ty_to_ts_type(
Some((rust_ty, false))
}
});
} else if rust_ty == "AsyncBlock" {
if let Some(arg) = args.first() {
ts_ty = Some((format!("Promise<{}>", arg.0), false));
} else {
// Not NAPI-RS `AsyncBlock`
ts_ty = Some((rust_ty, false));
}
} else if let Some(&(known_ty, _, _)) = KNOWN_TYPES.get(rust_ty.as_str()) {
if rust_ty == "()" && is_return_ty {
ts_ty = Some(("void".to_owned(), false));

View File

@ -347,6 +347,7 @@ impl Drop for Buffer {
/// SAFETY: This is undefined behavior, as the JS side may always modify the underlying buffer,
/// without synchronization. Also see the docs for the `AsMut` impl.
unsafe impl Send for Buffer {}
unsafe impl Sync for Buffer {}
impl Clone for Buffer {
fn clone(&self) -> Self {

View File

@ -6,7 +6,9 @@ use std::{
use tokio::runtime::Runtime;
use crate::{sys, Error, JsDeferred, JsUnknown, NapiValue, Result};
use crate::{
bindgen_runtime::ToNapiValue, sys, Env, Error, JsDeferred, JsUnknown, NapiValue, Result,
};
fn create_runtime() -> Option<Runtime> {
#[cfg(not(target_family = "wasm"))]
@ -221,3 +223,55 @@ pub fn execute_tokio_future<
Ok(promise.0.value)
}
pub struct AsyncBlockBuilder<
V: ToNapiValue + Send + 'static,
F: Future<Output = Result<V>> + Send + 'static,
Dispose: FnOnce(Env) + 'static,
> {
inner: F,
dispose: Option<Dispose>,
}
impl<
V: ToNapiValue + Send + 'static,
F: Future<Output = Result<V>> + Send + 'static,
Dispose: FnOnce(Env),
> AsyncBlockBuilder<V, F, Dispose>
{
pub fn with(inner: F) -> Self {
Self {
inner,
dispose: None,
}
}
pub fn with_dispose(mut self, dispose: Dispose) -> Self {
self.dispose = Some(dispose);
self
}
pub fn build(self, env: Env) -> Result<AsyncBlock<V>> {
Ok(AsyncBlock {
inner: execute_tokio_future(env.0, self.inner, |env, v| unsafe {
if let Some(dispose) = self.dispose {
let env = Env::from_raw(env);
dispose(env);
}
V::to_napi_value(env, v)
})?,
_phantom: PhantomData,
})
}
}
pub struct AsyncBlock<T: ToNapiValue + Send + 'static> {
inner: sys::napi_value,
_phantom: PhantomData<T>,
}
impl<T: ToNapiValue + Send + 'static> ToNapiValue for AsyncBlock<T> {
unsafe fn to_napi_value(_: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
Ok(val.inner)
}
}

View File

@ -335,6 +335,8 @@ Generated by [AVA](https://avajs.dev).
export declare function bufferPassThrough(buf: Buffer): Promise<Buffer>
export declare function bufferWithAsyncBlock(buf: Buffer): Promise<number>
export declare function buildThreadsafeFunctionFromFunction(callback: (arg0: number, arg1: number) => number): void␊
export declare function buildThreadsafeFunctionFromFunctionCalleeHandle(callback: () => void): void␊

View File

@ -359,29 +359,30 @@ function __napi_rs_initialize_modules(__napiInstance) {
__napiInstance.exports['__napi_register__mutate_typed_array_363']?.()
__napiInstance.exports['__napi_register__deref_uint8_array_364']?.()
__napiInstance.exports['__napi_register__buffer_pass_through_365']?.()
__napiInstance.exports['__napi_register__array_buffer_pass_through_366']?.()
__napiInstance.exports['__napi_register__accept_slice_367']?.()
__napiInstance.exports['__napi_register__accept_arraybuffer_368']?.()
__napiInstance.exports['__napi_register__create_arraybuffer_369']?.()
__napiInstance.exports['__napi_register__u8_array_to_array_370']?.()
__napiInstance.exports['__napi_register__i8_array_to_array_371']?.()
__napiInstance.exports['__napi_register__u16_array_to_array_372']?.()
__napiInstance.exports['__napi_register__i16_array_to_array_373']?.()
__napiInstance.exports['__napi_register__u32_array_to_array_374']?.()
__napiInstance.exports['__napi_register__i32_array_to_array_375']?.()
__napiInstance.exports['__napi_register__f32_array_to_array_376']?.()
__napiInstance.exports['__napi_register__f64_array_to_array_377']?.()
__napiInstance.exports['__napi_register__u64_array_to_array_378']?.()
__napiInstance.exports['__napi_register__i64_array_to_array_379']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_380']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_381']?.()
__napiInstance.exports['__napi_register__AsyncBuffer_impl_382']?.()
__napiInstance.exports['__napi_register__async_reduce_buffer_383']?.()
__napiInstance.exports['__napi_register__async_buffer_to_array_384']?.()
__napiInstance.exports['__napi_register__u_init8_array_from_string_385']?.()
__napiInstance.exports['__napi_register__AsyncReader_impl_386']?.()
__napiInstance.exports['__napi_register__Reader_struct_387']?.()
__napiInstance.exports['__napi_register__Reader_impl_389']?.()
__napiInstance.exports['__napi_register__buffer_with_async_block_366']?.()
__napiInstance.exports['__napi_register__array_buffer_pass_through_367']?.()
__napiInstance.exports['__napi_register__accept_slice_368']?.()
__napiInstance.exports['__napi_register__accept_arraybuffer_369']?.()
__napiInstance.exports['__napi_register__create_arraybuffer_370']?.()
__napiInstance.exports['__napi_register__u8_array_to_array_371']?.()
__napiInstance.exports['__napi_register__i8_array_to_array_372']?.()
__napiInstance.exports['__napi_register__u16_array_to_array_373']?.()
__napiInstance.exports['__napi_register__i16_array_to_array_374']?.()
__napiInstance.exports['__napi_register__u32_array_to_array_375']?.()
__napiInstance.exports['__napi_register__i32_array_to_array_376']?.()
__napiInstance.exports['__napi_register__f32_array_to_array_377']?.()
__napiInstance.exports['__napi_register__f64_array_to_array_378']?.()
__napiInstance.exports['__napi_register__u64_array_to_array_379']?.()
__napiInstance.exports['__napi_register__i64_array_to_array_380']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_381']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_382']?.()
__napiInstance.exports['__napi_register__AsyncBuffer_impl_383']?.()
__napiInstance.exports['__napi_register__async_reduce_buffer_384']?.()
__napiInstance.exports['__napi_register__async_buffer_to_array_385']?.()
__napiInstance.exports['__napi_register__u_init8_array_from_string_386']?.()
__napiInstance.exports['__napi_register__AsyncReader_impl_387']?.()
__napiInstance.exports['__napi_register__Reader_struct_388']?.()
__napiInstance.exports['__napi_register__Reader_impl_390']?.()
}
export const Animal = __napiModule.exports.Animal
export const AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
@ -453,6 +454,7 @@ export const bigintGetU64AsString = __napiModule.exports.bigintGetU64AsString
export const btreeSetToJs = __napiModule.exports.btreeSetToJs
export const btreeSetToRust = __napiModule.exports.btreeSetToRust
export const bufferPassThrough = __napiModule.exports.bufferPassThrough
export const bufferWithAsyncBlock = __napiModule.exports.bufferWithAsyncBlock
export const buildThreadsafeFunctionFromFunction = __napiModule.exports.buildThreadsafeFunctionFromFunction
export const buildThreadsafeFunctionFromFunctionCalleeHandle = __napiModule.exports.buildThreadsafeFunctionFromFunctionCalleeHandle
export const call0 = __napiModule.exports.call0

View File

@ -383,29 +383,30 @@ function __napi_rs_initialize_modules(__napiInstance) {
__napiInstance.exports['__napi_register__mutate_typed_array_363']?.()
__napiInstance.exports['__napi_register__deref_uint8_array_364']?.()
__napiInstance.exports['__napi_register__buffer_pass_through_365']?.()
__napiInstance.exports['__napi_register__array_buffer_pass_through_366']?.()
__napiInstance.exports['__napi_register__accept_slice_367']?.()
__napiInstance.exports['__napi_register__accept_arraybuffer_368']?.()
__napiInstance.exports['__napi_register__create_arraybuffer_369']?.()
__napiInstance.exports['__napi_register__u8_array_to_array_370']?.()
__napiInstance.exports['__napi_register__i8_array_to_array_371']?.()
__napiInstance.exports['__napi_register__u16_array_to_array_372']?.()
__napiInstance.exports['__napi_register__i16_array_to_array_373']?.()
__napiInstance.exports['__napi_register__u32_array_to_array_374']?.()
__napiInstance.exports['__napi_register__i32_array_to_array_375']?.()
__napiInstance.exports['__napi_register__f32_array_to_array_376']?.()
__napiInstance.exports['__napi_register__f64_array_to_array_377']?.()
__napiInstance.exports['__napi_register__u64_array_to_array_378']?.()
__napiInstance.exports['__napi_register__i64_array_to_array_379']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_380']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_381']?.()
__napiInstance.exports['__napi_register__AsyncBuffer_impl_382']?.()
__napiInstance.exports['__napi_register__async_reduce_buffer_383']?.()
__napiInstance.exports['__napi_register__async_buffer_to_array_384']?.()
__napiInstance.exports['__napi_register__u_init8_array_from_string_385']?.()
__napiInstance.exports['__napi_register__AsyncReader_impl_386']?.()
__napiInstance.exports['__napi_register__Reader_struct_387']?.()
__napiInstance.exports['__napi_register__Reader_impl_389']?.()
__napiInstance.exports['__napi_register__buffer_with_async_block_366']?.()
__napiInstance.exports['__napi_register__array_buffer_pass_through_367']?.()
__napiInstance.exports['__napi_register__accept_slice_368']?.()
__napiInstance.exports['__napi_register__accept_arraybuffer_369']?.()
__napiInstance.exports['__napi_register__create_arraybuffer_370']?.()
__napiInstance.exports['__napi_register__u8_array_to_array_371']?.()
__napiInstance.exports['__napi_register__i8_array_to_array_372']?.()
__napiInstance.exports['__napi_register__u16_array_to_array_373']?.()
__napiInstance.exports['__napi_register__i16_array_to_array_374']?.()
__napiInstance.exports['__napi_register__u32_array_to_array_375']?.()
__napiInstance.exports['__napi_register__i32_array_to_array_376']?.()
__napiInstance.exports['__napi_register__f32_array_to_array_377']?.()
__napiInstance.exports['__napi_register__f64_array_to_array_378']?.()
__napiInstance.exports['__napi_register__u64_array_to_array_379']?.()
__napiInstance.exports['__napi_register__i64_array_to_array_380']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_381']?.()
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_382']?.()
__napiInstance.exports['__napi_register__AsyncBuffer_impl_383']?.()
__napiInstance.exports['__napi_register__async_reduce_buffer_384']?.()
__napiInstance.exports['__napi_register__async_buffer_to_array_385']?.()
__napiInstance.exports['__napi_register__u_init8_array_from_string_386']?.()
__napiInstance.exports['__napi_register__AsyncReader_impl_387']?.()
__napiInstance.exports['__napi_register__Reader_struct_388']?.()
__napiInstance.exports['__napi_register__Reader_impl_390']?.()
}
module.exports.Animal = __napiModule.exports.Animal
module.exports.AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
@ -477,6 +478,7 @@ module.exports.bigintGetU64AsString = __napiModule.exports.bigintGetU64AsString
module.exports.btreeSetToJs = __napiModule.exports.btreeSetToJs
module.exports.btreeSetToRust = __napiModule.exports.btreeSetToRust
module.exports.bufferPassThrough = __napiModule.exports.bufferPassThrough
module.exports.bufferWithAsyncBlock = __napiModule.exports.bufferWithAsyncBlock
module.exports.buildThreadsafeFunctionFromFunction = __napiModule.exports.buildThreadsafeFunctionFromFunction
module.exports.buildThreadsafeFunctionFromFunctionCalleeHandle = __napiModule.exports.buildThreadsafeFunctionFromFunctionCalleeHandle
module.exports.call0 = __napiModule.exports.call0

View File

@ -434,6 +434,7 @@ module.exports.bigintGetU64AsString = nativeBinding.bigintGetU64AsString
module.exports.btreeSetToJs = nativeBinding.btreeSetToJs
module.exports.btreeSetToRust = nativeBinding.btreeSetToRust
module.exports.bufferPassThrough = nativeBinding.bufferPassThrough
module.exports.bufferWithAsyncBlock = nativeBinding.bufferWithAsyncBlock
module.exports.buildThreadsafeFunctionFromFunction = nativeBinding.buildThreadsafeFunctionFromFunction
module.exports.buildThreadsafeFunctionFromFunctionCalleeHandle = nativeBinding.buildThreadsafeFunctionFromFunctionCalleeHandle
module.exports.call0 = nativeBinding.call0

View File

@ -325,6 +325,8 @@ export declare function btreeSetToRust(set: Set<string>): void
export declare function bufferPassThrough(buf: Buffer): Promise<Buffer>
export declare function bufferWithAsyncBlock(buf: Buffer): Promise<number>
export declare function buildThreadsafeFunctionFromFunction(callback: (arg0: number, arg1: number) => number): void
export declare function buildThreadsafeFunctionFromFunctionCalleeHandle(callback: () => void): void

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use napi::{bindgen_prelude::*, JsArrayBuffer};
#[napi]
@ -73,6 +75,16 @@ async fn buffer_pass_through(buf: Buffer) -> Result<Buffer> {
Ok(buf)
}
#[napi]
fn buffer_with_async_block(env: Env, buf: Arc<Buffer>) -> Result<AsyncBlock<u32>> {
let buf_to_dispose = buf.clone();
AsyncBlockBuilder::with(async move { Ok(buf.len() as u32) })
.with_dispose(move |_| {
drop(buf_to_dispose);
})
.build(env)
}
#[napi]
async fn array_buffer_pass_through(buf: Uint8Array) -> Result<Uint8Array> {
Ok(buf)