use std::mem; use std::os::raw::{c_char, c_void}; use std::ptr; use crate::error::check_status; use crate::js_values::NapiValue; use crate::{sys, Env, Result, Task}; pub struct AsyncWork { inner_task: T, deferred: sys::napi_deferred, value: Result<*mut T::Output>, } impl AsyncWork { pub fn run(env: sys::napi_env, task: T, deferred: sys::napi_deferred) -> Result<()> { let mut raw_resource = ptr::null_mut(); check_status(unsafe { sys::napi_create_object(env, &mut raw_resource) })?; let mut raw_name = ptr::null_mut(); let s = "napi_rs_async"; check_status(unsafe { sys::napi_create_string_utf8( env, s.as_ptr() as *const c_char, s.len() as u64, &mut raw_name, ) })?; let result = AsyncWork { inner_task: task, deferred, value: Ok(ptr::null_mut()), }; let mut async_work = ptr::null_mut(); check_status(unsafe { sys::napi_create_async_work( env, raw_resource, raw_name, Some(execute:: as unsafe extern "C" fn(env: sys::napi_env, data: *mut c_void)), Some( complete:: as unsafe extern "C" fn( env: sys::napi_env, status: sys::napi_status, data: *mut c_void, ), ), Box::leak(Box::new(result)) as *mut _ as *mut c_void, &mut async_work, ) })?; check_status(unsafe { sys::napi_queue_async_work(env, async_work) }) } } unsafe impl Send for AsyncWork {} unsafe impl Sync for AsyncWork {} unsafe extern "C" fn execute(_env: sys::napi_env, data: *mut c_void) { let mut work = Box::from_raw(data as *mut AsyncWork); work.value = work .inner_task .compute() .map(|v| Box::into_raw(Box::from(v))); Box::leak(work); } unsafe extern "C" fn complete( env: sys::napi_env, status: sys::napi_status, data: *mut c_void, ) { let mut work = Box::from_raw(data as *mut AsyncWork); let value_ptr = mem::replace(&mut work.value, Ok(ptr::null_mut())); let deferred = mem::replace(&mut work.deferred, ptr::null_mut()); let value = value_ptr.and_then(move |v| { let mut env = Env::from_raw(env); let output = ptr::read(v as *const _); work.inner_task.resolve(&mut env, output) }); match check_status(status).and_then(move |_| value) { Ok(v) => { let status = sys::napi_resolve_deferred(env, deferred, v.raw_value()); debug_assert!(status == sys::napi_status::napi_ok, "Reject promise failed"); } Err(e) => { let status = sys::napi_reject_deferred(env, deferred, e.into_raw(env)); debug_assert!(status == sys::napi_status::napi_ok, "Reject promise failed"); } }; }