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}