1use std::cell::Cell;
2use std::marker::PhantomData;
3use std::mem;
4use std::os::raw::c_void;
5use std::panic::UnwindSafe;
6use std::ptr;
7use std::rc::Rc;
8
9use crate::bindgen_runtime::JsObjectValue;
10use crate::{
11 bindgen_runtime::{PromiseRaw, ToNapiValue},
12 check_status, sys, Env, Error, JsError, Result, ScopedTask, Status,
13};
14
15struct AsyncWork<'task, T: ScopedTask<'task>> {
16 inner_task: T,
17 deferred: sys::napi_deferred,
18 value: mem::MaybeUninit<Result<T::Output>>,
19 napi_async_work: sys::napi_async_work,
20 status: Rc<Cell<u8>>,
21}
22
23pub struct AsyncWorkPromise<T> {
24 pub(crate) napi_async_work: sys::napi_async_work,
25 raw_promise: sys::napi_value,
26 env: sys::napi_env,
27 pub(crate) status: Rc<Cell<u8>>,
32 _phantom: PhantomData<T>,
33}
34
35impl<T> UnwindSafe for AsyncWorkPromise<T> {}
36impl<T> std::panic::RefUnwindSafe for AsyncWorkPromise<T> {}
37
38impl<T> AsyncWorkPromise<T> {
39 pub fn promise_object<'env>(&self) -> PromiseRaw<'env, T> {
40 PromiseRaw::new(self.env, self.raw_promise)
41 }
42
43 pub fn cancel(&mut self) -> Result<()> {
44 self.status.set(2);
46 check_status!(
47 unsafe { sys::napi_cancel_async_work(self.env, self.napi_async_work) },
48 "Cancel async work failed"
49 )
50 }
51}
52
53pub fn run<'task, T: ScopedTask<'task>>(
54 env: sys::napi_env,
55 task: T,
56 abort_status: Option<Rc<Cell<u8>>>,
57) -> Result<AsyncWorkPromise<T::JsValue>> {
58 let mut undefined = ptr::null_mut();
59 check_status!(
60 unsafe { sys::napi_get_undefined(env, &mut undefined) },
61 "Get undefined failed in async_work::run"
62 )?;
63 let mut raw_promise = ptr::null_mut();
64 let mut deferred = ptr::null_mut();
65 check_status!(
66 unsafe { sys::napi_create_promise(env, &mut deferred, &mut raw_promise) },
67 "Create promise failed in async_work::run"
68 )?;
69 let task_status = abort_status.unwrap_or_else(|| Rc::new(Cell::new(0)));
70 let result = Box::leak(Box::new(AsyncWork {
71 inner_task: task,
72 deferred,
73 value: mem::MaybeUninit::uninit(),
74 napi_async_work: ptr::null_mut(),
75 status: task_status.clone(),
76 }));
77 check_status!(
78 unsafe {
79 sys::napi_create_async_work(
80 env,
81 raw_promise,
82 undefined,
83 Some(execute::<T>),
84 Some(complete::<T>),
85 (result as *mut AsyncWork<T>).cast(),
86 &mut result.napi_async_work,
87 )
88 },
89 "Create async work failed in async_work::run"
90 )?;
91 check_status!(
92 unsafe { sys::napi_queue_async_work(env, result.napi_async_work) },
93 "Queue async work failed in async_work::run"
94 )?;
95 Ok(AsyncWorkPromise {
96 napi_async_work: result.napi_async_work,
97 raw_promise,
98 env,
99 status: task_status,
100 _phantom: PhantomData,
101 })
102}
103
104unsafe impl<'task, T: ScopedTask<'task> + Send> Send for AsyncWork<'task, T> {}
105unsafe impl<'task, T: ScopedTask<'task> + Sync> Sync for AsyncWork<'task, T> {}
106
107unsafe extern "C" fn execute<'task, T: ScopedTask<'task>>(_env: sys::napi_env, data: *mut c_void) {
110 let work = Box::leak(unsafe { Box::from_raw(data as *mut AsyncWork<T>) });
111 let value = work.inner_task.compute();
112 work.value.write(value);
113}
114
115unsafe extern "C" fn complete<'task, T: ScopedTask<'task>>(
116 env: sys::napi_env,
117 status: sys::napi_status,
118 data: *mut c_void,
119) {
120 if let Err(e) = complete_impl::<T>(env, status, data) {
121 let js_err = JsError::from(e);
122 unsafe { js_err.throw_into(env) };
123 }
124}
125
126fn complete_impl<'task, T: ScopedTask<'task>>(
127 env: sys::napi_env,
128 status: sys::napi_status,
129 data: *mut c_void,
130) -> Result<()> {
131 let mut work = unsafe { Box::from_raw(data as *mut AsyncWork<T>) };
132 let napi_async_work = mem::replace(&mut work.napi_async_work, ptr::null_mut());
133 let deferred = mem::replace(&mut work.deferred, ptr::null_mut());
134 if status == sys::Status::napi_cancelled {
135 const ABORT_ERROR_NAME: &str = "AbortError";
136 let wrapped_env = Env::from_raw(env);
137 let mut error =
138 wrapped_env.create_error(Error::new(Status::Cancelled, ABORT_ERROR_NAME.to_owned()))?;
139 error.set_named_property("name", ABORT_ERROR_NAME)?;
140 check_status!(
141 unsafe { sys::napi_reject_deferred(env, deferred, error.0.value) },
142 "Reject AbortError failed"
143 )?;
144 } else {
145 let value_ptr = unsafe { work.value.assume_init() };
146 let value = match value_ptr {
147 Ok(output) => work.inner_task.resolve(
148 unsafe { std::mem::transmute::<&Env, &'task Env>(&Env::from_raw(env)) },
150 output,
151 ),
152 Err(e) => work.inner_task.reject(
153 unsafe { std::mem::transmute::<&Env, &'task Env>(&Env::from_raw(env)) },
155 e,
156 ),
157 };
158 if work.status.get() != 2 {
159 match check_status!(status)
160 .and_then(move |_| value)
161 .and_then(|v| unsafe { ToNapiValue::to_napi_value(env, v) })
162 {
163 Ok(v) => {
164 check_status!(
165 unsafe { sys::napi_resolve_deferred(env, deferred, v) },
166 "Resolve promise failed"
167 )?;
168 }
169 Err(e) => {
170 check_status!(
171 unsafe { sys::napi_reject_deferred(env, deferred, JsError::from(e).into_value(env)) },
172 "Reject promise failed"
173 )?;
174 }
175 };
176 }
177 work.status.set(1);
178 }
179 work.inner_task.finally(Env::from_raw(env))?;
180 check_status!(
181 unsafe { sys::napi_delete_async_work(env, napi_async_work) },
182 "Delete async work failed"
183 )?;
184 Ok(())
185}