1use std::{
11 ffi::c_void,
12 mem,
13 panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
14 ptr, thread,
15};
16
17use super::{
18 bindings as napi, debug_send_wrapper::DebugSendWrapper, no_panic::FailureBoundary, raw::Env,
19};
20
21const BOUNDARY: FailureBoundary = FailureBoundary {
22 both: "A panic and exception occurred while executing a `neon::event::TaskBuilder` task",
23 exception: "An exception occurred while executing a `neon::event::TaskBuilder` task",
24 panic: "A panic occurred while executing a `neon::event::TaskBuilder` task",
25};
26
27type Execute<I, O> = fn(input: I) -> O;
28type Complete<O, D> = fn(env: Env, output: thread::Result<O>, data: D);
29
30pub unsafe fn schedule<I, O, D>(
37 env: Env,
38 input: I,
39 execute: Execute<I, O>,
40 complete: Complete<O, D>,
41 data: D,
42) where
43 I: Send + 'static,
44 O: Send + 'static,
45 D: 'static,
46{
47 let mut data = Box::new(Data {
48 state: State::Input(input),
49 execute,
50 complete,
51 data: DebugSendWrapper::new(data),
52 work: ptr::null_mut(),
55 });
56
57 let work = &mut data.work as *mut _;
59
60 napi::create_async_work(
62 env,
63 ptr::null_mut(),
64 super::string(env, "neon_async_work"),
65 Some(call_execute::<I, O, D>),
66 Some(call_complete::<I, O, D>),
67 Box::into_raw(data).cast(),
68 work,
69 )
70 .unwrap();
71
72 match napi::queue_async_work(env, *work) {
74 Ok(()) => {}
75 status => {
76 let _ = napi::delete_async_work(env, *work);
78 status.unwrap()
79 }
80 }
81}
82
83struct Data<I, O, D> {
85 state: State<I, O>,
86 execute: Execute<I, O>,
87 complete: Complete<O, D>,
88 data: DebugSendWrapper<D>,
89 work: napi::AsyncWork,
90}
91
92enum State<I, O> {
94 Input(I),
96 Executing,
98 Output(thread::Result<O>),
100}
101
102impl<I, O> State<I, O> {
103 fn take_execute_input(&mut self) -> Option<I> {
105 match mem::replace(self, Self::Executing) {
106 Self::Input(input) => Some(input),
107 _ => None,
108 }
109 }
110
111 fn into_output(self) -> Option<thread::Result<O>> {
113 match self {
114 Self::Output(output) => Some(output),
115 _ => None,
116 }
117 }
118}
119
120unsafe extern "C" fn call_execute<I, O, D>(_: Env, data: *mut c_void) {
126 let data = &mut *data.cast::<Data<I, O, D>>();
127
128 let output = catch_unwind(AssertUnwindSafe(|| {
130 let input = data.state.take_execute_input().unwrap();
133
134 (data.execute)(input)
135 }));
136
137 data.state = State::Output(output);
138}
139
140unsafe extern "C" fn call_complete<I, O, D>(env: Env, status: napi::Status, data: *mut c_void) {
145 let Data {
146 state,
147 complete,
148 data,
149 work,
150 ..
151 } = *Box::<Data<I, O, D>>::from_raw(data.cast());
152
153 debug_assert_eq!(napi::delete_async_work(env, work), Ok(()));
154
155 BOUNDARY.catch_failure(env, None, move |env| {
156 let output = state.into_output().unwrap();
159
160 let env = if let Some(env) = env {
162 env
163 } else {
164 if let Err(panic) = output {
166 resume_unwind(panic);
167 }
168
169 return ptr::null_mut();
170 };
171
172 match status {
173 napi::Status::Ok => complete(env, output, data.take()),
174 napi::Status::Cancelled => {}
175 _ => assert_eq!(status, napi::Status::Ok),
176 }
177
178 ptr::null_mut()
179 });
180}