feat: upgrade to std-future
This commit is contained in:
parent
2cf731316b
commit
e0e85502af
30 changed files with 744 additions and 374 deletions
76
.github/workflows/linux.yaml
vendored
Normal file
76
.github/workflows/linux.yaml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
name: Linux
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ['10', '12', '13']
|
||||
version:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
name: ${{ matrix.version }} - x86_64-unknown-linux-gnu - node@${{ matrix.node }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install ${{ matrix.version }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}-x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Generate Cargo.lock
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: generate-lockfile
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: check build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all --bins --examples --tests -vvv
|
||||
|
||||
- name: tests
|
||||
uses: actions-rs/cargo@v1
|
||||
timeout-minutes: 40
|
||||
with:
|
||||
command: test
|
||||
args: -p napi-rs --lib -- --nocapture
|
||||
|
||||
- name: test scripts
|
||||
run: |
|
||||
cd test_module
|
||||
cargo build
|
||||
cp target/debug/libtest_module.so target/debug/libtest_module.node
|
||||
node tests.js
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||
cargo-cache
|
76
.github/workflows/macos.yaml
vendored
Normal file
76
.github/workflows/macos.yaml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
name: macOS
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ['10', '12', '13']
|
||||
version:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
name: ${{ matrix.version }} - x86_64-apple-darwin - node@${{ matrix.node }}
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install ${{ matrix.version }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}-x86_64-apple-darwin
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Generate Cargo.lock
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: generate-lockfile
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: check build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --all --bins --examples --tests -vvv
|
||||
|
||||
- name: tests
|
||||
uses: actions-rs/cargo@v1
|
||||
timeout-minutes: 40
|
||||
with:
|
||||
command: test
|
||||
args: -p napi-rs --lib -- --nocapture
|
||||
|
||||
- name: test scripts
|
||||
run: |
|
||||
cd test_module
|
||||
cargo build
|
||||
cp target/debug/libtest_module.dylib target/debug/libtest_module.node
|
||||
node tests.js
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||
cargo-cache
|
82
.github/workflows/windows.yaml
vendored
Normal file
82
.github/workflows/windows.yaml
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
name: Windows
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ['10', '12', '13']
|
||||
version:
|
||||
- stable
|
||||
- nightly
|
||||
target:
|
||||
- x86_64-pc-windows-msvc
|
||||
|
||||
name: ${{ matrix.version }} - ${{ matrix.target }} - node@${{ matrix.node }}
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- name: Install ${{ matrix.version }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}-${{ matrix.target }}
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: Install llvm
|
||||
run: choco install -y llvm
|
||||
- name: set environment variables
|
||||
uses: allenevans/set-env@v1.0.0
|
||||
with:
|
||||
LIBCLANG_PATH: 'C:\Program Files\LLVM\bin'
|
||||
- name: Generate Cargo.lock
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: generate-lockfile
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo index
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/git
|
||||
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ matrix.version }}-${{ matrix.target }}-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: check build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: -p napi-rs -vvv
|
||||
|
||||
- name: tests
|
||||
uses: actions-rs/cargo@v1
|
||||
timeout-minutes: 40
|
||||
with:
|
||||
command: test
|
||||
args: -p napi-rs --lib -- --nocapture
|
||||
|
||||
- name: test scripts
|
||||
run: |
|
||||
cd test_module
|
||||
cargo build
|
||||
cp target/debug/test_module.dll target/debug/libtest_module.node
|
||||
node tests.js
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
cargo install cargo-cache --no-default-features --features ci-autoclean
|
||||
cargo-cache
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,4 +3,5 @@ target/
|
|||
.DS_Store
|
||||
Cargo.lock
|
||||
node_modules
|
||||
src/sys/bindings.rs
|
||||
napi/src/sys/bindings.rs
|
||||
*.node
|
||||
|
|
1
.yarnrc
Normal file
1
.yarnrc
Normal file
|
@ -0,0 +1 @@
|
|||
registry "https://registry.npmjs.org"
|
20
Cargo.toml
20
Cargo.toml
|
@ -1,14 +1,6 @@
|
|||
[package]
|
||||
name = "napi-rs"
|
||||
version = "0.1.0"
|
||||
authors = ["Nathan Sobo <nathan@github.com>", "Yinan Long <lynweklm@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.1"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.52"
|
||||
cc = "1.0"
|
||||
glob = "0.3"
|
||||
semver = "0.9"
|
||||
[workspace]
|
||||
members = [
|
||||
"./build",
|
||||
"./napi"
|
||||
]
|
||||
exclude = ["./test_module"]
|
||||
|
|
20
README.md
20
README.md
|
@ -1,7 +1,25 @@
|
|||
# napi
|
||||
# napi-rs
|
||||
|
||||
> This project was initialized from [xray](https://github.com/atom/xray)
|
||||
|
||||
# Platform Support
|
||||
|
||||
![](https://github.com/Brooooooklyn/napi-rs/workflows/macOS/badge.svg)
|
||||
![](https://github.com/Brooooooklyn/napi-rs/workflows/Linux/badge.svg)
|
||||
![](https://github.com/Brooooooklyn/napi-rs/workflows/Windows/badge.svg)
|
||||
|
||||
## Operating Systems
|
||||
|
||||
| Linux | macOS | Windows x64 MSVC |
|
||||
| ----- | ----- | ---------------- |
|
||||
| ✓ | ✓ | ✓ |
|
||||
|
||||
## Python
|
||||
|
||||
| Node10 | Node 12 | Node13 |
|
||||
| --------- | --------- | --------- |
|
||||
| ✓ | ✓ | ✓ |
|
||||
|
||||
A minimal library for building compiled Node add-ons in Rust.
|
||||
|
||||
This library depends on N-API and requires Node 8.9 or later. It is still pretty raw and has not been tested in a production setting.
|
||||
|
|
87
build.rs
87
build.rs
|
@ -1,87 +0,0 @@
|
|||
extern crate bindgen;
|
||||
extern crate cc;
|
||||
extern crate glob;
|
||||
extern crate semver;
|
||||
|
||||
use glob::glob;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
fn find_it<P>(exe_name: P) -> Option<PathBuf>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
env::var_os("PATH").and_then(|paths| {
|
||||
env::split_paths(&paths)
|
||||
.filter_map(|dir| {
|
||||
let full_path = dir.join(&exe_name);
|
||||
if full_path.is_file() {
|
||||
Some(full_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let node_include_path = find_it("node")
|
||||
.expect("can not find executable node")
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("include/node");
|
||||
let node_version = semver::Version::parse(
|
||||
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.get(1..)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let node_major_version = node_version.major;
|
||||
|
||||
println!("cargo:rerun-if-env-changed=NODE_INCLUDE_PATH");
|
||||
for entry in glob("./src/sys/**/*.*").unwrap() {
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
entry.unwrap().to_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// Activate the "node8" or "nodestable" feature for compatibility with
|
||||
// different versions of Node.js/N-API.
|
||||
println!(
|
||||
"cargo:rustc-cfg=node{}",
|
||||
if node_major_version > 8 {
|
||||
"stable"
|
||||
} else if node_major_version == 8 {
|
||||
"8"
|
||||
} else {
|
||||
panic!("node version is too low")
|
||||
}
|
||||
);
|
||||
|
||||
bindgen::Builder::default()
|
||||
.header("src/sys/bindings.h")
|
||||
.clang_arg(String::from("-I") + node_include_path.to_str().unwrap())
|
||||
.rustified_enum("(napi_|uv_).+")
|
||||
.whitelist_function("(napi_|uv_|extras_).+")
|
||||
.whitelist_type("(napi_|uv_|extras_).+")
|
||||
.generate()
|
||||
.expect("Unable to generate napi bindings")
|
||||
.write_to_file("src/sys/bindings.rs")
|
||||
.expect("Unable to write napi bindings");
|
||||
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include(&node_include_path)
|
||||
.file("src/sys/bindings.cc")
|
||||
.flag("-std=c++0x")
|
||||
.flag("-Wno-unused-parameter")
|
||||
.compile("extras");
|
||||
}
|
13
build/Cargo.toml
Normal file
13
build/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "napi-build"
|
||||
version = "0.1.0"
|
||||
authors = ["LongYinan <lynweklm@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
reqwest = { version = "0.10", features = ["native-tls", "blocking"] }
|
1
build/build.rs
Normal file
1
build/build.rs
Normal file
|
@ -0,0 +1 @@
|
|||
fn main() {}
|
67
build/src/lib.rs
Normal file
67
build/src/lib.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
extern crate cfg_if;
|
||||
#[cfg(windows)]
|
||||
extern crate reqwest;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(windows)] {
|
||||
use std::env::var;
|
||||
use std::fs::File;
|
||||
use std::io::copy;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn setup() {
|
||||
let node_full_version =
|
||||
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap();
|
||||
let node_exec_path = String::from_utf8(
|
||||
Command::new("node")
|
||||
.arg("-e")
|
||||
.arg("console.log(process.execPath)")
|
||||
.output()
|
||||
.unwrap()
|
||||
.stdout,
|
||||
)
|
||||
.unwrap();
|
||||
let node_exec_path = node_exec_path.trim_end();
|
||||
let mut node_lib_file_dir = PathBuf::from(node_exec_path)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf();
|
||||
let node_lib_dir = PathBuf::from(&node_lib_file_dir);
|
||||
node_lib_file_dir.push(format!("node-{}.lib", node_full_version.trim_end()));
|
||||
if !node_lib_file_dir.exists() {
|
||||
let lib_file_download_url = format!(
|
||||
"https://nodejs.org/dist/{}/win-x64/node.lib",
|
||||
node_full_version
|
||||
);
|
||||
let mut resp =
|
||||
reqwest::blocking::get(&lib_file_download_url).expect("Download node.lib file failed");
|
||||
let mut node_lib_file = File::create(&node_lib_file_dir).unwrap();
|
||||
copy(&mut resp, &mut node_lib_file).expect("Save node.lib file failed");
|
||||
}
|
||||
println!(
|
||||
"cargo:rustc-link-lib={}",
|
||||
&node_lib_file_dir.file_stem().unwrap().to_str().unwrap()
|
||||
);
|
||||
println!("cargo:rustc-link-search={}", node_lib_dir.to_str().unwrap());
|
||||
// Link `win_delay_load_hook.obj` for windows electron
|
||||
let node_runtime_env = "npm_config_runtime";
|
||||
println!("cargo:rerun-if-env-changed={}", node_runtime_env);
|
||||
if var(node_runtime_env).map(|s| s == "electron") == Ok(true) {
|
||||
println!("cargo:rustc-cdylib-link-arg=win_delay_load_hook.obj");
|
||||
println!("cargo:rustc-cdylib-link-arg=delayimp.lib");
|
||||
println!("cargo:rustc-cdylib-link-arg=/DELAYLOAD:node.exe");
|
||||
}
|
||||
}
|
||||
} else if #[cfg(target_os = "macos")] {
|
||||
/// Set up the build environment by setting Cargo configuration variables.
|
||||
pub fn setup() {
|
||||
println!("cargo:rustc-cdylib-link-arg=-undefined");
|
||||
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
|
||||
}
|
||||
} else {
|
||||
pub fn setup() { }
|
||||
}
|
||||
}
|
21
napi/Cargo.toml
Normal file
21
napi/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "napi-rs"
|
||||
version = "0.1.0"
|
||||
authors = ["Nathan Sobo <nathan@github.com>", "Yinan Long <lynweklm@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
futures = { version = "0.3", features = ["default", "thread-pool"] }
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
flate2 = "1.0"
|
||||
reqwest = { version = "0.10", features = ["native-tls", "blocking"] }
|
||||
tar = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.53.1"
|
||||
cc = "1.0"
|
||||
glob = "0.3"
|
||||
napi-build = { path = "../build" }
|
||||
regex = "1.3"
|
||||
semver = "0.9"
|
170
napi/build.rs
Normal file
170
napi/build.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
extern crate bindgen;
|
||||
extern crate cc;
|
||||
#[cfg(windows)]
|
||||
extern crate flate2;
|
||||
extern crate glob;
|
||||
extern crate napi_build;
|
||||
#[cfg(windows)]
|
||||
extern crate reqwest;
|
||||
extern crate semver;
|
||||
#[cfg(windows)]
|
||||
extern crate tar;
|
||||
|
||||
use glob::glob;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
// https://stackoverflow.com/questions/37498864/finding-executable-in-path-with-rust
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
|
||||
exe_name.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn enhance_exe_name(exe_name: &Path) -> Cow<Path> {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
||||
let raw_input: Vec<_> = exe_name.as_os_str().encode_wide().collect();
|
||||
let raw_extension: Vec<_> = OsStr::new(".exe").encode_wide().collect();
|
||||
|
||||
if raw_input.ends_with(&raw_extension) {
|
||||
exe_name.into()
|
||||
} else {
|
||||
let mut with_exe = exe_name.as_os_str().to_owned();
|
||||
with_exe.push(".exe");
|
||||
PathBuf::from(with_exe).into()
|
||||
}
|
||||
}
|
||||
|
||||
fn find_it<P>(exe_name: P) -> Option<PathBuf>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let exe_name = enhance_exe_name(exe_name.as_ref());
|
||||
env::var_os("PATH").and_then(|paths| {
|
||||
env::split_paths(&paths)
|
||||
.filter_map(|dir| {
|
||||
let full_path = dir.join(&exe_name);
|
||||
if full_path.is_file() {
|
||||
Some(full_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
let node_full_version =
|
||||
String::from_utf8(Command::new("node").arg("-v").output().unwrap().stdout).unwrap();
|
||||
let node_version = semver::Version::parse(node_full_version.as_str().get(1..).unwrap()).unwrap();
|
||||
|
||||
let node_major_version = node_version.major;
|
||||
|
||||
println!("cargo:rerun-if-env-changed=NODE_INCLUDE_PATH");
|
||||
for entry in glob("./src/sys/**/*.*").unwrap() {
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
entry.unwrap().to_str().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
env::set_var("CARGO_RUSTC_FLAGS", "-Clink-args=-export_dynamic");
|
||||
|
||||
if node_major_version < 10 {
|
||||
panic!("node version is too low")
|
||||
}
|
||||
|
||||
let node_include_path = find_node_include_path(node_full_version.trim_end());
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
let mut sys_bindigs_path = PathBuf::from("src");
|
||||
sys_bindigs_path.push("sys");
|
||||
sys_bindigs_path.push("bindings.h");
|
||||
|
||||
bindgen::Builder::default()
|
||||
.header(sys_bindigs_path.to_str().unwrap().to_owned())
|
||||
.clang_arg(String::from("-I") + node_include_path.to_str().unwrap())
|
||||
.rustified_enum("(napi_|uv_).+")
|
||||
.whitelist_function("(napi_|uv_|extras_).+")
|
||||
.whitelist_type("(napi_|uv_|extras_).+")
|
||||
.generate()
|
||||
.expect("Unable to generate napi bindings")
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Unable to write napi bindings");
|
||||
|
||||
let mut bindings_path = PathBuf::from("src");
|
||||
bindings_path.push("sys");
|
||||
bindings_path.push("bindings.cc");
|
||||
|
||||
let mut cc_builder = cc::Build::new();
|
||||
|
||||
cc_builder
|
||||
.cpp(true)
|
||||
.include(&node_include_path)
|
||||
.file(bindings_path);
|
||||
if !cfg!(windows) {
|
||||
cc_builder.flag("-Wno-unused-parameter");
|
||||
};
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
cc_builder.flag("-std=c++0x");
|
||||
} else if cfg!(linux) || cfg!(target_env = "gnu") {
|
||||
cc_builder.flag("-std=c++14");
|
||||
}
|
||||
|
||||
cc_builder
|
||||
.cargo_metadata(true)
|
||||
.out_dir(&out_path)
|
||||
.compile("napi-bindings");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn find_node_include_path(node_full_version: &str) -> PathBuf {
|
||||
let mut node_exec_path = PathBuf::from(
|
||||
find_it("node")
|
||||
.expect("can not find executable node")
|
||||
.parent()
|
||||
.unwrap(),
|
||||
);
|
||||
node_exec_path.push(format!("node-headers-{}.tar.gz", node_full_version));
|
||||
let mut header_dist_path = PathBuf::from(&PathBuf::from(&node_exec_path).parent().unwrap());
|
||||
let unpack_path = PathBuf::from(&header_dist_path);
|
||||
header_dist_path.push(format!("node-{}", node_full_version));
|
||||
header_dist_path.push("include");
|
||||
header_dist_path.push("node");
|
||||
if !header_dist_path.exists() {
|
||||
let header_file_download_url = String::from_utf8(
|
||||
Command::new("node")
|
||||
.args(vec!["-e", "console.log(process.release.headersUrl)"])
|
||||
.output()
|
||||
.unwrap()
|
||||
.stdout,
|
||||
)
|
||||
.unwrap();
|
||||
let resp = reqwest::blocking::get(&header_file_download_url).expect("request failed");
|
||||
tar::Archive::new(flate2::read::GzDecoder::new(resp))
|
||||
.unpack(&unpack_path)
|
||||
.expect("Unpack headers file failed");
|
||||
};
|
||||
header_dist_path
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn find_node_include_path(_node_full_version: &str) -> PathBuf {
|
||||
let node_exec_path = find_it("node").expect("can not find executable node");
|
||||
node_exec_path
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("include/node")
|
||||
}
|
106
napi/src/executor.rs
Normal file
106
napi/src/executor.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use super::sys;
|
||||
use futures::task::Poll;
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
pub struct LibuvExecutor {
|
||||
event_loop: *mut sys::uv_loop_s,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct LibuvWaker(*mut sys::uv_async_t);
|
||||
|
||||
unsafe impl Send for LibuvWaker {}
|
||||
|
||||
unsafe impl Sync for LibuvWaker {}
|
||||
|
||||
const UV_ASYNC_V_TABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
clone_executor,
|
||||
wake_uv_async,
|
||||
wake_uv_async_by_ref,
|
||||
drop_uv_async,
|
||||
);
|
||||
|
||||
unsafe fn clone_executor(uv_async_t: *const ()) -> RawWaker {
|
||||
RawWaker::new(uv_async_t, &UV_ASYNC_V_TABLE)
|
||||
}
|
||||
|
||||
unsafe fn wake_uv_async(uv_async_t: *const ()) {
|
||||
let status = sys::uv_async_send(uv_async_t as *mut sys::uv_async_t);
|
||||
assert!(status == 0, "wake_uv_async failed");
|
||||
}
|
||||
|
||||
unsafe fn wake_uv_async_by_ref(uv_async_t: *const ()) {
|
||||
let status = sys::uv_async_send(uv_async_t as *mut sys::uv_async_t);
|
||||
assert!(status == 0, "wake_uv_async_by_ref failed");
|
||||
}
|
||||
|
||||
unsafe fn drop_uv_async(uv_async_t_ptr: *const ()) {
|
||||
sys::uv_unref(uv_async_t_ptr as *mut sys::uv_handle_t);
|
||||
}
|
||||
|
||||
struct Task<'a> {
|
||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||
context: Context<'a>,
|
||||
}
|
||||
|
||||
impl LibuvExecutor {
|
||||
pub fn new(event_loop: *mut sys::uv_loop_s) -> Self {
|
||||
Self { event_loop }
|
||||
}
|
||||
|
||||
pub fn execute<F: 'static + Future<Output = ()>>(&self, future: F) {
|
||||
let uninit = mem::MaybeUninit::<sys::uv_async_t>::uninit();
|
||||
let uv_async_t: Box<sys::uv_async_t> = unsafe { Box::new(uninit.assume_init()) };
|
||||
let uv_async_t_ref = Box::leak(uv_async_t);
|
||||
unsafe {
|
||||
let status = sys::uv_async_init(self.event_loop, uv_async_t_ref, Some(poll_future));
|
||||
assert!(status == 0, "Non-zero status returned from uv_async_init");
|
||||
};
|
||||
unsafe {
|
||||
let waker = Waker::from_raw(RawWaker::new(
|
||||
uv_async_t_ref as *mut _ as *const (),
|
||||
&UV_ASYNC_V_TABLE,
|
||||
));
|
||||
let context = Context::from_waker(&waker);
|
||||
let task = Box::leak(Box::new(Task {
|
||||
future: Box::pin(future),
|
||||
context,
|
||||
}));
|
||||
sys::uv_handle_set_data(
|
||||
uv_async_t_ref as *mut _ as *mut sys::uv_handle_t,
|
||||
task as *mut _ as *mut c_void,
|
||||
);
|
||||
task.poll_future();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Task<'a> {
|
||||
fn poll_future(&mut self) -> bool {
|
||||
match self.future.as_mut().poll(&mut self.context) {
|
||||
Poll::Ready(_) => true,
|
||||
Poll::Pending => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn poll_future(handle: *mut sys::uv_async_t) {
|
||||
let data_ptr = sys::uv_handle_get_data(handle as *mut sys::uv_handle_t) as *mut Task;
|
||||
let mut task = Box::from_raw(data_ptr);
|
||||
if !task.as_mut().poll_future() {
|
||||
Box::leak(task);
|
||||
} else {
|
||||
sys::uv_close(
|
||||
handle as *mut sys::uv_handle_t,
|
||||
Some(drop_handle_after_close),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
unsafe extern "C" fn drop_handle_after_close(handle: *mut sys::uv_handle_t) {
|
||||
Box::from_raw(handle);
|
||||
}
|
|
@ -54,7 +54,7 @@ pub struct Function;
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Buffer {
|
||||
data: *const u8,
|
||||
size: usize,
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -160,14 +160,15 @@ macro_rules! callback {
|
|||
) -> sys::napi_value {
|
||||
const MAX_ARGC: usize = 8;
|
||||
let mut argc = MAX_ARGC;
|
||||
let mut raw_args: [$crate::sys::napi_value; MAX_ARGC] = unsafe { mem::uninitialized() };
|
||||
let mut raw_args =
|
||||
unsafe { mem::MaybeUninit::<[$crate::sys::napi_value; MAX_ARGC]>::uninit().assume_init() };
|
||||
let mut raw_this = ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
let status = sys::napi_get_cb_info(
|
||||
raw_env,
|
||||
cb_info,
|
||||
&mut argc,
|
||||
&mut argc as *mut usize as *mut u64,
|
||||
&mut raw_args[0],
|
||||
&mut raw_this,
|
||||
ptr::null_mut(),
|
||||
|
@ -177,8 +178,8 @@ macro_rules! callback {
|
|||
|
||||
let env = Env::from_raw(raw_env);
|
||||
let this = Value::from_raw(&env, raw_this);
|
||||
let mut args: [Value<Any>; 8] = unsafe { mem::uninitialized() };
|
||||
for (i, raw_arg) in raw_args.into_iter().enumerate() {
|
||||
let mut args = unsafe { mem::MaybeUninit::<[Value<Any>; 8]>::uninit().assume_init() };
|
||||
for (i, raw_arg) in raw_args.iter().enumerate() {
|
||||
args[i] = Value::from_raw(&env, *raw_arg)
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,12 @@ impl Env {
|
|||
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Value<'a, String> {
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let status = unsafe {
|
||||
sys::napi_create_string_utf8(self.0, s.as_ptr() as *const c_char, s.len(), &mut raw_value)
|
||||
sys::napi_create_string_utf8(
|
||||
self.0,
|
||||
s.as_ptr() as *const c_char,
|
||||
s.len() as u64,
|
||||
&mut raw_value,
|
||||
)
|
||||
};
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
Value::from_raw_value(self, raw_value, String)
|
||||
|
@ -255,8 +261,9 @@ impl Env {
|
|||
|
||||
pub fn create_string_utf16<'a, 'b>(&'a self, chars: &[u16]) -> Value<'a, String> {
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let status =
|
||||
unsafe { sys::napi_create_string_utf16(self.0, chars.as_ptr(), chars.len(), &mut raw_value) };
|
||||
let status = unsafe {
|
||||
sys::napi_create_string_utf16(self.0, chars.as_ptr(), chars.len() as u64, &mut raw_value)
|
||||
};
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
Value::from_raw_value(self, raw_value, String)
|
||||
}
|
||||
|
@ -270,12 +277,13 @@ impl Env {
|
|||
|
||||
pub fn create_array_with_length(&self, length: usize) -> Value<Object> {
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let status = unsafe { sys::napi_create_array_with_length(self.0, length, &mut raw_value) };
|
||||
let status =
|
||||
unsafe { sys::napi_create_array_with_length(self.0, length as u64, &mut raw_value) };
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
Value::from_raw_value(self, raw_value, Object)
|
||||
}
|
||||
|
||||
pub fn create_buffer(&self, length: usize) -> Value<Buffer> {
|
||||
pub fn create_buffer(&self, length: u64) -> Value<Buffer> {
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let mut data = ptr::null_mut();
|
||||
let status = unsafe { sys::napi_create_buffer(self.0, length, &mut data, &mut raw_value) };
|
||||
|
@ -290,7 +298,7 @@ impl Env {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn create_buffer_with_data(&self, data_ptr: *const u8, length: usize) -> Value<Buffer> {
|
||||
pub fn create_buffer_with_data(&self, data_ptr: *const u8, length: u64) -> Value<Buffer> {
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let mut data_raw_ptr = data_ptr as *mut c_void;
|
||||
let status =
|
||||
|
@ -316,7 +324,7 @@ impl Env {
|
|||
sys::napi_create_function(
|
||||
self.0,
|
||||
name.as_ptr() as *const c_char,
|
||||
name.len(),
|
||||
name.len() as u64,
|
||||
Some(callback),
|
||||
callback as *mut c_void,
|
||||
&mut raw_result,
|
||||
|
@ -368,10 +376,10 @@ impl Env {
|
|||
sys::napi_define_class(
|
||||
self.0,
|
||||
name.as_ptr() as *const c_char,
|
||||
name.len(),
|
||||
name.len() as u64,
|
||||
Some(constructor_cb),
|
||||
ptr::null_mut(),
|
||||
raw_properties.len(),
|
||||
raw_properties.len() as u64,
|
||||
raw_properties.as_ptr(),
|
||||
&mut raw_result,
|
||||
)
|
||||
|
@ -559,7 +567,7 @@ impl ValueType for Object {
|
|||
impl ValueType for Buffer {
|
||||
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
||||
let mut data = ptr::null_mut();
|
||||
let mut size: usize = 0;
|
||||
let mut size: u64 = 0;
|
||||
let status = unsafe { sys::napi_get_buffer_info(env, raw, &mut data, &mut size) };
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
Buffer {
|
||||
|
@ -664,10 +672,10 @@ impl<'env, T: ValueType> Value<'env, T> {
|
|||
#[inline]
|
||||
fn get_raw_type(env: sys::napi_env, raw_value: sys::napi_value) -> sys::napi_valuetype {
|
||||
unsafe {
|
||||
let mut value_type: sys::napi_valuetype = mem::uninitialized();
|
||||
let status = sys::napi_typeof(env, raw_value, &mut value_type);
|
||||
let value_type = ptr::null_mut();
|
||||
let status = sys::napi_typeof(env, raw_value, value_type);
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
value_type
|
||||
*value_type
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -692,7 +700,7 @@ impl<'env> Deref for Value<'env, String> {
|
|||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
let mut written_char_count: usize = 0;
|
||||
let mut written_char_count: u64 = 0;
|
||||
let len = self.len() + 1;
|
||||
let mut result = Vec::with_capacity(len);
|
||||
unsafe {
|
||||
|
@ -700,14 +708,14 @@ impl<'env> Deref for Value<'env, String> {
|
|||
self.env.0,
|
||||
self.raw_value,
|
||||
result.as_mut_ptr(),
|
||||
len,
|
||||
&mut written_char_count as *mut usize,
|
||||
len as u64,
|
||||
&mut written_char_count,
|
||||
);
|
||||
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
let ptr = result.as_ptr();
|
||||
mem::forget(result);
|
||||
slice::from_raw_parts(ptr as *const u8, written_char_count)
|
||||
slice::from_raw_parts(ptr as *const u8, written_char_count as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -722,11 +730,11 @@ impl<'env> Into<Vec<u16>> for Value<'env, String> {
|
|||
self.env.0,
|
||||
self.raw_value,
|
||||
result.as_mut_ptr(),
|
||||
result.capacity(),
|
||||
result.capacity() as u64,
|
||||
&mut written_char_count,
|
||||
);
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
result.set_len(written_char_count);
|
||||
result.set_len(written_char_count as usize);
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -871,7 +879,7 @@ impl<'env> Deref for Value<'env, Buffer> {
|
|||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.value.data, self.value.size) }
|
||||
unsafe { slice::from_raw_parts(self.value.data, self.value.size as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -882,7 +890,7 @@ impl<'env> DerefMut for Value<'env, Buffer> {
|
|||
let status =
|
||||
unsafe { sys::napi_get_buffer_info(self.env.0, self.raw_value, &mut data, &mut size) };
|
||||
debug_assert!(Status::from(status) == Status::Ok);
|
||||
unsafe { slice::from_raw_parts_mut(data as *mut _, size) }
|
||||
unsafe { slice::from_raw_parts_mut(data as *mut _, size as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -895,7 +903,7 @@ impl<'env> Value<'env, Function> {
|
|||
let raw_this = this
|
||||
.map(|v| v.into_raw())
|
||||
.unwrap_or_else(|| self.env.get_undefined().into_raw());
|
||||
let mut raw_args: [sys::napi_value; 8] = unsafe { mem::uninitialized() };
|
||||
let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() };
|
||||
for (i, arg) in args.into_iter().enumerate() {
|
||||
raw_args[i] = arg.raw_value;
|
||||
}
|
||||
|
@ -905,7 +913,7 @@ impl<'env> Value<'env, Function> {
|
|||
self.env.0,
|
||||
raw_this,
|
||||
self.raw_value,
|
||||
args.len(),
|
||||
args.len() as u64,
|
||||
&raw_args[0],
|
||||
&mut return_value,
|
||||
)
|
|
@ -10,7 +10,9 @@ static v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v)
|
|||
return local;
|
||||
}
|
||||
|
||||
void extras_open_callback_scope(napi_async_context napi_async_context,
|
||||
EXTERN_C_START
|
||||
|
||||
NAPI_EXTERN void extras_open_callback_scope(napi_async_context napi_async_context,
|
||||
napi_value napi_resource_object,
|
||||
extras_callback_scope *result)
|
||||
{
|
||||
|
@ -21,7 +23,9 @@ void extras_open_callback_scope(napi_async_context napi_async_context,
|
|||
*result = reinterpret_cast<extras_callback_scope>(new node::CallbackScope(isolate, resource_object, *node_async_context));
|
||||
}
|
||||
|
||||
void extras_close_callback_scope(extras_callback_scope callback_scope)
|
||||
NAPI_EXTERN void extras_close_callback_scope(extras_callback_scope callback_scope)
|
||||
{
|
||||
delete reinterpret_cast<node::CallbackScope *>(callback_scope);
|
||||
}
|
||||
|
||||
EXTERN_C_END
|
|
@ -3,14 +3,7 @@
|
|||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
include!("bindings.rs");
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
#[cfg(node8)]
|
||||
mod node8;
|
||||
#[cfg(node8)]
|
||||
pub use self::node8::Status;
|
||||
|
||||
#[cfg(nodestable)]
|
||||
mod stable;
|
||||
#[cfg(nodestable)]
|
||||
pub use self::stable::Status;
|
10
package.json
10
package.json
|
@ -5,9 +5,6 @@
|
|||
"bin": {
|
||||
"napi": "scripts/napi.js"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "scripts/napi.js check"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:Brooooooklyn/napi-rs.git"
|
||||
|
@ -35,13 +32,10 @@
|
|||
"arrowParens": "always"
|
||||
},
|
||||
"files": [
|
||||
"scripts/napi.js",
|
||||
"src/**",
|
||||
"build.rs",
|
||||
"Cargo.toml",
|
||||
"Cargo.lock"
|
||||
"scripts/napi.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^13.7.1",
|
||||
"prettier": "^1.12.1"
|
||||
}
|
||||
}
|
||||
|
|
15
scripts/napi.js
Executable file → Normal file
15
scripts/napi.js
Executable file → Normal file
|
@ -6,10 +6,9 @@ const path = require('path')
|
|||
const os = require('os')
|
||||
const parsedNodeVersion = process.versions.node.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
||||
const nodeMajorVersion = parseInt(parsedNodeVersion[1])
|
||||
const nodeMinorVersion = parseInt(parsedNodeVersion[2])
|
||||
|
||||
if (nodeMajorVersion < 8 || (nodeMajorVersion === 8 && nodeMinorVersion < 9)) {
|
||||
console.error('This build script should be run on Node 8.9 or greater')
|
||||
if (nodeMajorVersion < 10) {
|
||||
console.error('This build script should be run on Node 10 or greater')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
|
@ -19,17 +18,7 @@ const argv = parseArgs(process.argv.slice(2), {
|
|||
|
||||
const subcommand = argv._[0] || 'build'
|
||||
|
||||
const nodeIncludePath = path.join(
|
||||
process.argv[0],
|
||||
'..',
|
||||
'..',
|
||||
'include',
|
||||
'node',
|
||||
)
|
||||
|
||||
const moduleName = path.basename(process.cwd()).replace(/-/g, '_')
|
||||
process.env.NODE_INCLUDE_PATH = nodeIncludePath
|
||||
process.env.NODE_MAJOR_VERSION = nodeMajorVersion > 8 ? 'stable' : 8
|
||||
|
||||
const platform = os.platform()
|
||||
let libExt, platformArgs
|
||||
|
|
125
src/executor.rs
125
src/executor.rs
|
@ -1,125 +0,0 @@
|
|||
use super::sys;
|
||||
use futures::executor::{self, Notify, Spawn};
|
||||
use futures::future::{ExecuteError, Executor};
|
||||
use futures::{Async, Future};
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub struct LibuvExecutor {
|
||||
event_loop: *mut sys::uv_loop_t,
|
||||
}
|
||||
|
||||
struct Task<T: 'static + Future> {
|
||||
spawn: Spawn<T>,
|
||||
notify_handle: Arc<TaskNotifyHandle>,
|
||||
}
|
||||
|
||||
struct TaskNotifyHandle(RwLock<Option<UvAsyncHandle>>);
|
||||
|
||||
struct UvAsyncHandle(Box<sys::uv_async_t>);
|
||||
|
||||
impl LibuvExecutor {
|
||||
pub fn new(event_loop: *mut sys::uv_loop_t) -> Self {
|
||||
Self { event_loop }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Executor<F> for LibuvExecutor
|
||||
where
|
||||
F: 'static + Future<Item = (), Error = ()>,
|
||||
{
|
||||
fn execute(&self, future: F) -> Result<(), ExecuteError<F>> {
|
||||
let spawn = executor::spawn(future);
|
||||
|
||||
unsafe {
|
||||
let mut task = Box::new(Task {
|
||||
spawn,
|
||||
notify_handle: mem::uninitialized(),
|
||||
});
|
||||
|
||||
ptr::write(
|
||||
&mut task.notify_handle,
|
||||
Arc::new(TaskNotifyHandle::new(
|
||||
self.event_loop,
|
||||
Some(poll_future_on_main_thread::<F>),
|
||||
mem::transmute_copy(&task),
|
||||
)),
|
||||
);
|
||||
|
||||
if !task.poll_future() {
|
||||
mem::forget(task)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Future> Task<T> {
|
||||
fn poll_future(&mut self) -> bool {
|
||||
match self.spawn.poll_future_notify(&self.notify_handle, 0) {
|
||||
Ok(Async::Ready(_)) => {
|
||||
let handle = self.notify_handle.0.write().unwrap().take().unwrap();
|
||||
handle.close();
|
||||
true
|
||||
}
|
||||
Ok(Async::NotReady) => false,
|
||||
Err(_) => panic!("Future yielded an error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskNotifyHandle {
|
||||
fn new(event_loop: *mut sys::uv_loop_t, callback: sys::uv_async_cb, data: *mut c_void) -> Self {
|
||||
TaskNotifyHandle(RwLock::new(Some(UvAsyncHandle::new(
|
||||
event_loop, callback, data,
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
impl Notify for TaskNotifyHandle {
|
||||
fn notify(&self, _id: usize) {
|
||||
if let Some(ref uv_handle) = *self.0.read().unwrap() {
|
||||
unsafe {
|
||||
sys::uv_async_send(mem::transmute_copy(&uv_handle.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UvAsyncHandle {
|
||||
fn new(event_loop: *mut sys::uv_loop_t, callback: sys::uv_async_cb, data: *mut c_void) -> Self {
|
||||
unsafe {
|
||||
let mut handle = UvAsyncHandle(Box::new(mem::uninitialized()));
|
||||
let status = sys::uv_async_init(event_loop, mem::transmute_copy(&handle.0), callback);
|
||||
assert!(status == 0, "Non-zero status returned from uv_async_init");
|
||||
handle.0.data = data;
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
fn close(self) {
|
||||
unsafe {
|
||||
sys::uv_close(mem::transmute_copy(&self.0), Some(drop_handle_after_close));
|
||||
mem::forget(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for UvAsyncHandle {}
|
||||
unsafe impl Sync for UvAsyncHandle {}
|
||||
|
||||
extern "C" fn drop_handle_after_close(handle: *mut sys::uv_handle_t) {
|
||||
unsafe {
|
||||
Box::from_raw(handle);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn poll_future_on_main_thread<T: 'static + Future>(handle: *mut sys::uv_async_t) {
|
||||
let mut task: Box<Task<T>> = unsafe { Box::from_raw((*handle).data as *mut Task<T>) };
|
||||
if !task.poll_future() {
|
||||
mem::forget(task); // Don't drop task if it isn't complete.
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
use super::napi_status;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub enum Status {
|
||||
Ok,
|
||||
InvalidArg,
|
||||
ObjectExpected,
|
||||
StringExpected,
|
||||
NameExpected,
|
||||
FunctionExpected,
|
||||
NumberExpected,
|
||||
BooleanExpected,
|
||||
ArrayExpected,
|
||||
GenericFailure,
|
||||
PendingException,
|
||||
Cancelled,
|
||||
EscapeCalledTwice,
|
||||
HandleScopeMismatch,
|
||||
StringContainsNull,
|
||||
CallbackScopeMismatch,
|
||||
}
|
||||
|
||||
impl From<napi_status> for Status {
|
||||
fn from(code: napi_status) -> Self {
|
||||
use self::napi_status::*;
|
||||
use Status::*;
|
||||
|
||||
match code {
|
||||
napi_ok => Ok,
|
||||
napi_invalid_arg => InvalidArg,
|
||||
napi_object_expected => ObjectExpected,
|
||||
napi_string_expected => StringExpected,
|
||||
napi_name_expected => NameExpected,
|
||||
napi_function_expected => FunctionExpected,
|
||||
napi_number_expected => NumberExpected,
|
||||
napi_boolean_expected => BooleanExpected,
|
||||
napi_array_expected => ArrayExpected,
|
||||
napi_generic_failure => GenericFailure,
|
||||
napi_pending_exception => PendingException,
|
||||
napi_cancelled => Cancelled,
|
||||
napi_escape_called_twice => EscapeCalledTwice,
|
||||
napi_handle_scope_mismatch => HandleScopeMismatch,
|
||||
napi_callback_scope_mismatch => CallbackScopeMismatch,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,13 @@
|
|||
name = "test-module"
|
||||
version = "0.1.0"
|
||||
authors = ["LongYinan <lynweklm@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["dylib"]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
napi-rs = {path = ".."}
|
||||
napi-rs = {path = "../napi"}
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { path = "../build" }
|
||||
|
|
7
test_module/build.rs
Normal file
7
test_module/build.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
use napi_build::setup;
|
||||
|
||||
setup();
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
"name": "test-module",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "napi build",
|
||||
"build-release": "napi build --release",
|
||||
"check": "napi check",
|
||||
"build": "../scripts/napi.js build",
|
||||
"build-release": "../scripts/napi.js build --release",
|
||||
"check": "../scripts/napi.js check",
|
||||
"test": "node ./tests.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -25,41 +25,29 @@ fn test_spawn<'a>(
|
|||
_this: Value<'a, Any>,
|
||||
_args: &[Value<'a, Any>],
|
||||
) -> Result<Option<Value<'a, Any>>> {
|
||||
use futures::future::Executor;
|
||||
use futures::{Future, Stream};
|
||||
use std::{thread, time};
|
||||
use futures::executor::ThreadPool;
|
||||
use futures::StreamExt;
|
||||
|
||||
let async_context = env.async_init(None, "test_spawn");
|
||||
let pool = ThreadPool::new().expect("Failed to build pool");
|
||||
let (promise, deferred) = env.create_promise();
|
||||
let (tx, rx) = futures::sync::mpsc::unbounded();
|
||||
|
||||
let future = rx.for_each(|n: usize| {
|
||||
println!("Received value {:?}", n);
|
||||
futures::future::ok(())
|
||||
}).and_then(move |_| {
|
||||
let (tx, rx) = futures::channel::mpsc::unbounded::<i32>();
|
||||
let fut_values = async move {
|
||||
let fut_tx_result = async move {
|
||||
(0..100).for_each(|v| {
|
||||
tx.unbounded_send(v).expect("Failed to send");
|
||||
})
|
||||
};
|
||||
pool.spawn_ok(fut_tx_result);
|
||||
let fut_values = rx.map(|v| v * 2).collect::<Vec<i32>>();
|
||||
let results = fut_values.await;
|
||||
println!("Collected result lenght {}", results.len());
|
||||
async_context.enter(|env| {
|
||||
env.resolve_deferred(deferred, env.get_undefined());
|
||||
});
|
||||
futures::future::ok(())
|
||||
});
|
||||
};
|
||||
|
||||
env.create_executor().execute(future).unwrap();
|
||||
|
||||
for _i in 0..10 {
|
||||
let thread_tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let mut n = 0;
|
||||
loop {
|
||||
println!("send {:?}", n);
|
||||
thread_tx.unbounded_send(n).unwrap();
|
||||
n += 1;
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
if n == 10 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
env.create_executor().execute(fut_values);
|
||||
|
||||
Ok(Some(promise.try_into().unwrap()))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const testModule = require('./target/debug/test_module')
|
||||
const testModule = require(`./target/debug/libtest_module.node`)
|
||||
|
||||
function testSpawn() {
|
||||
console.log('=== Test spawning a future on libuv event loop')
|
||||
|
@ -16,4 +16,12 @@ function testThrow() {
|
|||
process.exit(1)
|
||||
}
|
||||
|
||||
testSpawn().then(testThrow)
|
||||
const future = testSpawn()
|
||||
|
||||
// https://github.com/nodejs/node/issues/29355
|
||||
setTimeout(() => {
|
||||
future.then(testThrow).catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
}, 10)
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
napi-rs@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/napi-rs/-/napi-rs-0.1.0.tgz#32c8b57045e939d66bbf1a0d5d36dc0cf188d70c"
|
||||
version "0.1.1"
|
||||
resolved "https://registry.npmjs.org/napi-rs/-/napi-rs-0.1.1.tgz#00c823423be5f070e4d5a01b143e4e848f4a2512"
|
||||
integrity sha512-eYHtHpqneoL3coE0kLr5hbRR9Z3hx97qfNdzXneuUWR3eFs8qNaJZRX2dln2gWieJaYrqUWs+rvR4+jdBlse6Q==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -2,10 +2,17 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@^13.7.1":
|
||||
version "13.7.1"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d"
|
||||
integrity sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
prettier@^1.12.1:
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
|
||||
version "1.19.1"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
|
Loading…
Reference in a new issue