diff --git a/.github/workflows/napi3.yaml b/.github/workflows/napi3.yaml index f9ea7eb6..5548f8ce 100644 --- a/.github/workflows/napi3.yaml +++ b/.github/workflows/napi3.yaml @@ -7,7 +7,7 @@ on: jobs: build_and_test: - name: stable - x86_64-unknown-linux-gnu - node@10.0 + name: stable - x86_64-unknown-linux-gnu - node@10.2 runs-on: ubuntu-latest steps: @@ -15,9 +15,9 @@ jobs: - name: Setup node run: | - wget https://nodejs.org/dist/v10.0.0/node-v10.0.0-linux-x64.tar.xz - tar xf node-v10.0.0-linux-x64.tar.xz - echo "::add-path::$(pwd)/node-v10.0.0-linux-x64/bin" + wget https://nodejs.org/dist/v10.2.0/node-v10.2.0-linux-x64.tar.xz + tar xf node-v10.2.0-linux-x64.tar.xz + echo "::add-path::$(pwd)/node-v10.2.0-linux-x64/bin" - name: Install stable uses: actions-rs/toolchain@v1 diff --git a/napi/src/call_context.rs b/napi/src/call_context.rs index 2f2fa095..823d4caa 100644 --- a/napi/src/call_context.rs +++ b/napi/src/call_context.rs @@ -4,7 +4,7 @@ use crate::error::check_status; use crate::{sys, Either, Env, Error, JsUndefined, NapiValue, Result, Status}; pub struct CallContext<'env> { - pub env: &'env Env, + pub env: &'env mut Env, raw_this: sys::napi_value, callback_info: sys::napi_callback_info, args: &'env [sys::napi_value], @@ -14,7 +14,7 @@ pub struct CallContext<'env> { impl<'env> CallContext<'env> { pub fn new( - env: &'env Env, + env: &'env mut Env, callback_info: sys::napi_callback_info, raw_this: sys::napi_value, args: &'env [sys::napi_value], diff --git a/napi/src/cleanup_env.rs b/napi/src/cleanup_env.rs new file mode 100644 index 00000000..4670208f --- /dev/null +++ b/napi/src/cleanup_env.rs @@ -0,0 +1,8 @@ +pub(crate) struct CleanupEnvHookData { + pub(crate) data: T, + pub(crate) hook: Box ()>, +} + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct CleanupEnvHook(pub(crate) *mut CleanupEnvHookData); diff --git a/napi/src/env.rs b/napi/src/env.rs index 26ffac17..e8f14862 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -5,6 +5,7 @@ use std::mem; use std::os::raw::{c_char, c_void}; use std::ptr; +use super::cleanup_env::{CleanupEnvHook, CleanupEnvHookData}; use crate::async_work::{self, AsyncWorkPromise}; use crate::error::check_status; use crate::js_values::*; @@ -455,13 +456,12 @@ impl Env { let tagged_object: *mut TaggedObject = mem::transmute(unknown_tagged_object); (*tagged_object).object.as_mut().ok_or(Error { status: Status::InvalidArg, - reason: "Invalid argument, nothing attach to js_external".to_owned(), + reason: "nothing attach to js_external".to_owned(), }) } else { Err(Error { status: Status::InvalidArg, - reason: "Invalid argument, T on get_value_external is not the type of wrapped object" - .to_owned(), + reason: "T on get_value_external is not the type of wrapped object".to_owned(), }) } } @@ -519,6 +519,41 @@ impl Env { Ok(uv_loop) } + #[cfg(napi3)] + pub fn add_env_cleanup_hook( + &mut self, + cleanup_data: T, + cleanup_fn: F, + ) -> Result> + where + T: 'static, + F: 'static + FnOnce(T) -> (), + { + let hook = CleanupEnvHookData { + data: cleanup_data, + hook: Box::new(cleanup_fn), + }; + let hook_ref = Box::leak(Box::new(hook)); + check_status(unsafe { + sys::napi_add_env_cleanup_hook( + self.0, + Some(cleanup_env::), + hook_ref as *mut CleanupEnvHookData as *mut _, + ) + })?; + Ok(CleanupEnvHook(hook_ref)) + } + + #[cfg(napi3)] + pub fn remove_env_cleanup_hook(&mut self, hook: CleanupEnvHook) -> Result<()> + where + T: 'static, + { + check_status(unsafe { + sys::napi_remove_env_cleanup_hook(self.0, Some(cleanup_env::), hook.0 as *mut _) + }) + } + #[cfg(all(feature = "libuv", napi4))] pub fn execute< T: 'static + Send, @@ -665,3 +700,8 @@ unsafe extern "C" fn raw_finalize( let tagged_object: *mut TaggedObject = mem::transmute(finalize_data); Box::from_raw(tagged_object); } + +unsafe extern "C" fn cleanup_env(hook_data: *mut c_void) { + let cleanup_env_hook = Box::from_raw(hook_data as *mut CleanupEnvHookData); + (cleanup_env_hook.hook)(cleanup_env_hook.data); +} diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 8eb69b0a..d5352128 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -92,6 +92,7 @@ mod async_work; mod call_context; +mod cleanup_env; mod env; mod error; mod js_values; @@ -100,6 +101,8 @@ mod module; mod promise; mod status; mod task; +#[cfg(napi3)] +pub use cleanup_env::CleanupEnvHook; #[cfg(napi4)] pub mod threadsafe_function; #[cfg(all(feature = "tokio_rt", napi4))] diff --git a/napi/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs index 92e0b1eb..5dd3f741 100644 --- a/napi/src/threadsafe_function.rs +++ b/napi/src/threadsafe_function.rs @@ -124,15 +124,14 @@ impl ThreadsafeFunction { pub fn create(env: &Env, func: JsFunction, to_js: T, max_queue_size: u64) -> Result { let mut async_resource_name = ptr::null_mut(); let s = "napi_rs_threadsafe_function"; - let status = unsafe { + check_status(unsafe { sys::napi_create_string_utf8( env.0, s.as_ptr() as *const c_char, s.len() as u64, &mut async_resource_name, ) - }; - check_status(status)?; + })?; let initial_thread_count: u64 = 1; let mut result = ptr::null_mut(); diff --git a/test_module/__test__/cleanup-env.spec.ts b/test_module/__test__/cleanup-env.spec.ts new file mode 100644 index 00000000..ce013f9b --- /dev/null +++ b/test_module/__test__/cleanup-env.spec.ts @@ -0,0 +1,17 @@ +import test from 'ava' + +const bindings = require('../index.node') + +test('should be able to add cleanup hook', (t) => { + t.notThrows(() => { + const ret = bindings.addCleanupHook() + t.is(typeof ret, 'object') + }) +}) + +test('should be able to remove cleanup hook', (t) => { + t.notThrows(() => { + const ret = bindings.addCleanupHook() + bindings.removeCleanupHook(ret) + }) +}) diff --git a/test_module/src/cleanup_env.rs b/test_module/src/cleanup_env.rs new file mode 100644 index 00000000..ff55b9a5 --- /dev/null +++ b/test_module/src/cleanup_env.rs @@ -0,0 +1,27 @@ +use napi::{ + CallContext, CleanupEnvHook, ContextlessResult, Env, JsExternal, JsUndefined, Module, Result, +}; + +#[contextless_function] +fn add_cleanup_hook(mut env: Env) -> ContextlessResult { + let hook = env.add_env_cleanup_hook((), |_| { + println!("cleanup hook executed"); + })?; + env.create_external(hook).map(Some) +} + +#[js_function(1)] +fn remove_cleanup_hook(ctx: CallContext) -> Result { + let hook_external = ctx.get::(0)?; + let hook = *ctx + .env + .get_value_external::>(&hook_external)?; + ctx.env.remove_env_cleanup_hook(hook)?; + ctx.env.get_undefined() +} + +pub fn register_js(module: &mut Module) -> Result<()> { + module.create_named_method("addCleanupHook", add_cleanup_hook)?; + module.create_named_method("removeCleanupHook", remove_cleanup_hook)?; + Ok(()) +} diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index 4c0ccabc..863c8365 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -7,6 +7,8 @@ extern crate serde_derive; use napi::{Module, Result}; +#[cfg(napi3)] +mod cleanup_env; #[cfg(napi4)] mod libuv; #[cfg(napi4)] @@ -54,6 +56,8 @@ fn init(module: &mut Module) -> Result<()> { env::register_js(module)?; object::register_js(module)?; global::register_js(module)?; + #[cfg(napi3)] + cleanup_env::register_js(module)?; #[cfg(napi4)] napi4::register_js(module)?; #[cfg(napi4)]