|
| 1 | +use std::ffi::c_void; |
| 2 | +use std::mem; |
| 3 | +use std::ptr; |
| 4 | + |
| 5 | +use crate::napi::bindings as napi; |
| 6 | +use crate::raw::Env; |
| 7 | + |
| 8 | +type Execute<T, O> = fn(input: T) -> O; |
| 9 | +type Complete<O> = fn(env: Env, output: O); |
| 10 | + |
| 11 | +/// Schedule work to execute on the libuv thread pool |
| 12 | +/// |
| 13 | +/// # Safety |
| 14 | +/// * `env` must be a valid `napi_env` for the current thread |
| 15 | +pub unsafe fn schedule<T, O>(env: Env, input: T, execute: Execute<T, O>, complete: Complete<O>) |
| 16 | +where |
| 17 | + T: Send + 'static, |
| 18 | + O: Send + 'static, |
| 19 | +{ |
| 20 | + let mut data = Box::new(Data { |
| 21 | + state: State::Input(input), |
| 22 | + execute, |
| 23 | + complete, |
| 24 | + work: ptr::null_mut(), |
| 25 | + }); |
| 26 | + |
| 27 | + let work = &mut data.work as *mut _; |
| 28 | + |
| 29 | + assert_eq!( |
| 30 | + napi::create_async_work( |
| 31 | + env, |
| 32 | + ptr::null_mut(), |
| 33 | + super::string(env, "neon_async_work"), |
| 34 | + Some(call_execute::<T, O>), |
| 35 | + Some(call_complete::<T, O>), |
| 36 | + Box::into_raw(data).cast(), |
| 37 | + work, |
| 38 | + ), |
| 39 | + napi::Status::Ok, |
| 40 | + ); |
| 41 | + |
| 42 | + match napi::queue_async_work(env, *work) { |
| 43 | + napi::Status::Ok => {} |
| 44 | + status => { |
| 45 | + napi::delete_async_work(env, *work); |
| 46 | + assert_eq!(status, napi::Status::Ok); |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +struct Data<T, O> { |
| 52 | + state: State<T, O>, |
| 53 | + execute: Execute<T, O>, |
| 54 | + complete: Complete<O>, |
| 55 | + work: napi::AsyncWork, |
| 56 | +} |
| 57 | + |
| 58 | +enum State<T, O> { |
| 59 | + Input(T), |
| 60 | + Output(O), |
| 61 | + Empty, |
| 62 | +} |
| 63 | + |
| 64 | +impl<T, O> State<T, O> { |
| 65 | + fn take_input(&mut self) -> Option<T> { |
| 66 | + match mem::replace(self, Self::Empty) { |
| 67 | + Self::Input(input) => Some(input), |
| 68 | + _ => None, |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + fn take_output(&mut self) -> Option<O> { |
| 73 | + match mem::replace(self, Self::Empty) { |
| 74 | + Self::Output(output) => Some(output), |
| 75 | + _ => None, |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +unsafe extern "C" fn call_execute<T, O>(_: Env, data: *mut c_void) { |
| 81 | + let data = &mut *data.cast::<Data<T, O>>(); |
| 82 | + let input = data.state.take_input().unwrap(); |
| 83 | + let output = (data.execute)(input); |
| 84 | + |
| 85 | + data.state = State::Output(output); |
| 86 | +} |
| 87 | + |
| 88 | +unsafe extern "C" fn call_complete<T, O>(env: Env, status: napi::Status, data: *mut c_void) { |
| 89 | + let Data { |
| 90 | + mut state, |
| 91 | + complete, |
| 92 | + work, |
| 93 | + .. |
| 94 | + } = *Box::<Data<T, O>>::from_raw(data.cast()); |
| 95 | + |
| 96 | + napi::delete_async_work(env, work); |
| 97 | + |
| 98 | + match status { |
| 99 | + napi::Status::Ok => complete(env, state.take_output().unwrap()), |
| 100 | + napi::Status::Cancelled => {} |
| 101 | + _ => assert_eq!(status, napi::Status::Ok), |
| 102 | + } |
| 103 | +} |
0 commit comments