Skip to content

Commit 0803489

Browse files
committed
Initial implementation for JsPromise and TaskBuilder
1 parent cb884ee commit 0803489

File tree

24 files changed

+704
-54
lines changed

24 files changed

+704
-54
lines changed

.cargo/config.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[alias]
22
# Neon defines mutually exclusive feature flags which prevents using `cargo clippy --all-features`
33
# The following aliases simplify linting the entire workspace
4-
check-napi = "check --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental"
4+
check-napi = "check --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental,promise-api,task-api"
55
check-legacy = "check --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p tests -p static_tests --features event-handler-api,proc-macros,try-catch-api,legacy-runtime"
66
clippy-legacy = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p tests -p static_tests --features event-handler-api,proc-macros,try-catch-api,legacy-runtime -- -A clippy::missing_safety_doc"
77
clippy-napi = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental -- -A clippy::missing_safety_doc"
88
neon-test = "test --no-default-features --features napi-experimental"
9-
neon-doc = "rustdoc --no-default-features --features=channel-api,napi-experimental,proc-macros,try-catch-api -- --cfg docsrs"
9+
neon-doc = "rustdoc --no-default-features --features=channel-api,napi-experimental,proc-macros,try-catch-api,promise-api,task-api -- --cfg docsrs"

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ event-queue-api = ["channel-api"]
7878
# Feature flag to include procedural macros
7979
proc-macros = ["neon-macros"]
8080

81+
# Enable `JsPromise` and `Deferred`
82+
# https://github.com/neon-bindings/rfcs/pull/35
83+
promise-api = []
84+
# Enable `TaskBuilder`
85+
# https://github.com/neon-bindings/rfcs/pull/35
86+
task-api = []
87+
8188
[package.metadata.docs.rs]
8289
no-default-features = true
8390
rustdoc-args = ["--cfg", "docsrs"]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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+
}

crates/neon-runtime/src/napi/bindings/functions.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ mod napi1 {
5757
fn is_buffer(env: Env, value: Value, result: *mut bool) -> Status;
5858
fn is_error(env: Env, value: Value, result: *mut bool) -> Status;
5959
fn is_array(env: Env, value: Value, result: *mut bool) -> Status;
60+
fn is_promise(env: Env, value: Value, result: *mut bool) -> Status;
6061

6162
fn get_value_string_utf8(
6263
env: Env,
@@ -209,6 +210,22 @@ mod napi1 {
209210
) -> Status;
210211

211212
fn run_script(env: Env, script: Value, result: *mut Value) -> Status;
213+
214+
fn create_async_work(
215+
env: Env,
216+
async_resource: Value,
217+
async_resource_name: Value,
218+
execute: AsyncExecuteCallback,
219+
complete: AsyncCompleteCallback,
220+
data: *mut c_void,
221+
result: *mut AsyncWork,
222+
) -> Status;
223+
224+
fn delete_async_work(env: Env, work: AsyncWork) -> Status;
225+
fn queue_async_work(env: Env, work: AsyncWork) -> Status;
226+
fn create_promise(env: Env, deferred: *mut Deferred, promise: *mut Value) -> Status;
227+
fn resolve_deferred(env: Env, deferred: Deferred, resolution: Value) -> Status;
228+
fn reject_deferred(env: Env, deferred: Deferred, rejection: Value) -> Status;
212229
}
213230
);
214231
}

crates/neon-runtime/src/napi/bindings/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ macro_rules! generate {
169169
use std::sync::Once;
170170

171171
pub(crate) use functions::*;
172-
pub use types::TypedArrayType;
173172
pub(crate) use types::*;
173+
pub use types::{Deferred, TypedArrayType};
174174

175175
mod functions;
176176
mod types;

crates/neon-runtime/src/napi/bindings/types.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub type CallbackInfo = *mut CallbackInfo__;
2929
pub struct EscapableHandleScope__ {
3030
_unused: [u8; 0],
3131
}
32+
3233
pub type EscapableHandleScope = *mut EscapableHandleScope__;
3334

3435
#[repr(C)]
@@ -203,3 +204,24 @@ impl std::ops::BitAndAssign for KeyFilter {
203204
self.0 &= rhs.0;
204205
}
205206
}
207+
208+
#[repr(C)]
209+
#[derive(Debug, Copy, Clone)]
210+
pub struct AsyncWork__ {
211+
_unused: [u8; 0],
212+
}
213+
214+
pub type AsyncWork = *mut AsyncWork__;
215+
216+
pub type AsyncExecuteCallback = Option<unsafe extern "C" fn(env: Env, data: *mut c_void)>;
217+
218+
pub type AsyncCompleteCallback =
219+
Option<unsafe extern "C" fn(env: Env, status: Status, data: *mut c_void)>;
220+
221+
#[repr(C)]
222+
#[derive(Debug, Copy, Clone)]
223+
pub struct Deferred__ {
224+
_unused: [u8; 0],
225+
}
226+
227+
pub type Deferred = *mut Deferred__;

crates/neon-runtime/src/napi/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod array;
22
pub mod arraybuffer;
3+
pub mod async_work;
34
pub mod buffer;
45
pub mod call;
56
pub mod convert;
@@ -13,6 +14,7 @@ pub mod lifecycle;
1314
pub mod mem;
1415
pub mod object;
1516
pub mod primitive;
17+
pub mod promise;
1618
pub mod raw;
1719
pub mod reference;
1820
pub mod scope;
@@ -23,4 +25,24 @@ pub mod tsfn;
2325
pub mod typedarray;
2426

2527
mod bindings;
28+
2629
pub use bindings::*;
30+
31+
use std::mem::MaybeUninit;
32+
33+
unsafe fn string(env: Env, s: impl AsRef<str>) -> raw::Local {
34+
let s = s.as_ref();
35+
let mut result = MaybeUninit::uninit();
36+
37+
assert_eq!(
38+
create_string_utf8(
39+
env,
40+
s.as_bytes().as_ptr() as *const _,
41+
s.len(),
42+
result.as_mut_ptr(),
43+
),
44+
Status::Ok,
45+
);
46+
47+
result.assume_init()
48+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use std::mem::MaybeUninit;
2+
use std::ptr;
3+
4+
use crate::napi::bindings as napi;
5+
use crate::raw::Env;
6+
7+
pub unsafe fn create(env: Env) -> (napi::Deferred, napi::Value) {
8+
let mut deferred = MaybeUninit::uninit();
9+
let mut promise = MaybeUninit::uninit();
10+
11+
assert_eq!(
12+
napi::create_promise(env, deferred.as_mut_ptr(), promise.as_mut_ptr()),
13+
napi::Status::Ok,
14+
);
15+
16+
(deferred.assume_init(), promise.assume_init())
17+
}
18+
19+
pub unsafe fn resolve(env: Env, deferred: napi::Deferred, resolution: napi::Value) {
20+
assert_eq!(
21+
napi::resolve_deferred(env, deferred, resolution),
22+
napi::Status::Ok,
23+
);
24+
}
25+
26+
pub unsafe fn reject(env: Env, deferred: napi::Deferred, rejection: napi::Value) {
27+
assert_eq!(
28+
napi::reject_deferred(env, deferred, rejection),
29+
napi::Status::Ok,
30+
);
31+
}
32+
33+
pub unsafe fn reject_err_message(env: Env, deferred: napi::Deferred, msg: impl AsRef<str>) {
34+
let msg = super::string(env, msg);
35+
let mut err = MaybeUninit::uninit();
36+
37+
assert_eq!(
38+
napi::create_error(env, ptr::null_mut(), msg, err.as_mut_ptr()),
39+
napi::Status::Ok,
40+
);
41+
42+
reject(env, deferred, err.assume_init());
43+
}

crates/neon-runtime/src/napi/tag.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,13 @@ pub unsafe fn is_date(env: Env, val: Local) -> bool {
9999
);
100100
result
101101
}
102+
103+
/// Is `val` a Promise?
104+
pub unsafe fn is_promise(env: Env, val: Local) -> bool {
105+
let mut result = false;
106+
assert_eq!(
107+
napi::is_promise(env, val, &mut result as *mut _),
108+
napi::Status::Ok
109+
);
110+
result
111+
}

crates/neon-runtime/src/napi/tsfn.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,7 @@ use std::mem::MaybeUninit;
55
use std::sync::{Arc, Mutex};
66

77
use crate::napi::bindings as napi;
8-
use crate::raw::{Env, Local};
9-
10-
unsafe fn string(env: Env, s: impl AsRef<str>) -> Local {
11-
let s = s.as_ref();
12-
let mut result = MaybeUninit::uninit();
13-
14-
assert_eq!(
15-
napi::create_string_utf8(
16-
env,
17-
s.as_bytes().as_ptr() as *const _,
18-
s.len(),
19-
result.as_mut_ptr(),
20-
),
21-
napi::Status::Ok,
22-
);
23-
24-
result.assume_init()
25-
}
8+
use crate::raw::Env;
269

2710
#[derive(Debug)]
2811
struct Tsfn(napi::ThreadsafeFunction);
@@ -85,7 +68,7 @@ impl<T: Send + 'static> ThreadsafeFunction<T> {
8568
env,
8669
std::ptr::null_mut(),
8770
std::ptr::null_mut(),
88-
string(env, "neon threadsafe function"),
71+
super::string(env, "neon threadsafe function"),
8972
max_queue_size,
9073
// Always set the reference count to 1. Prefer using
9174
// Rust `Arc` to maintain the struct.

0 commit comments

Comments
 (0)