fix(napi): memory issues in create_external_buffer

This commit is contained in:
LongYinan 2020-12-21 16:10:30 +08:00
parent d2b61ef01f
commit 0446e463c8
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
7 changed files with 76 additions and 10 deletions

View file

@ -11,6 +11,12 @@ crate-type = ["cdylib"]
napi = {path = "../napi"} napi = {path = "../napi"}
napi-derive = {path = "../napi-derive"} napi-derive = {path = "../napi-derive"}
[target.'cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))'.dependencies]
jemallocator = {version = "0.3", features = ["disable_initial_exec_tls"]}
[target.'cfg(windows)'.dependencies]
mimalloc = {version = "0.1"}
[build-dependencies] [build-dependencies]
napi-build = {path = "../build"} napi-build = {path = "../build"}

View file

@ -4,11 +4,17 @@ import { join } from 'path'
import { Summary } from 'benny/lib/internal/common-types' import { Summary } from 'benny/lib/internal/common-types'
import { benchAsync } from './async' import { benchAsync } from './async'
import { benchBuffer } from './buffer'
import { benchNoop } from './noop' import { benchNoop } from './noop'
import { benchPlus } from './plus' import { benchPlus } from './plus'
async function run() { async function run() {
const output = [await benchNoop(), await benchPlus(), await benchAsync()] const output = [
await benchNoop(),
await benchPlus(),
await benchBuffer(),
await benchAsync(),
]
.map(formatSummary) .map(formatSummary)
.join('\n') .join('\n')

23
bench/buffer.ts Normal file
View file

@ -0,0 +1,23 @@
import b from 'benny'
const { benchCreateBuffer } = require('./index.node')
function createBuffer() {
const buf = Buffer.alloc(100000)
buf[0] = 1
buf[1] = 2
return buf
}
export const benchBuffer = () =>
b.suite(
'Create buffer',
b.add('napi-rs', () => {
benchCreateBuffer()
}),
b.add('JavaScript', () => {
createBuffer()
}),
b.cycle(),
b.complete(),
)

16
bench/src/buffer.rs Normal file
View file

@ -0,0 +1,16 @@
use napi::{ContextlessResult, Env, JsBuffer, JsObject, Result};
#[contextless_function]
pub fn bench_create_buffer(env: Env) -> ContextlessResult<JsBuffer> {
let mut output = Vec::with_capacity(100000);
output.push(1);
output.push(2);
env
.create_buffer_with_data(output)
.map(|v| Some(v.into_raw()))
}
pub fn register_js(exports: &mut JsObject) -> Result<()> {
exports.create_named_method("benchCreateBuffer", bench_create_buffer)?;
Ok(())
}

View file

@ -3,7 +3,16 @@ extern crate napi_derive;
use napi::{JsObject, Result}; use napi::{JsObject, Result};
#[cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[cfg(windows)]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
mod async_compute; mod async_compute;
mod buffer;
mod noop; mod noop;
mod plus; mod plus;
@ -12,6 +21,7 @@ fn init(mut exports: JsObject) -> Result<()> {
exports.create_named_method("noop", noop::noop)?; exports.create_named_method("noop", noop::noop)?;
async_compute::register_js(&mut exports)?; async_compute::register_js(&mut exports)?;
buffer::register_js(&mut exports)?;
plus::register_js(&mut exports)?; plus::register_js(&mut exports)?;
Ok(()) Ok(())

View file

@ -1,7 +1,8 @@
{ {
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext" "target": "ESNext",
"rootDir": "."
}, },
"include": ["."], "include": ["."],
"exclude": ["target", "src"] "exclude": ["target", "src"]

View file

@ -284,7 +284,7 @@ impl Env {
/// This API allocates a node::Buffer object and initializes it with data backed by the passed in buffer. /// This API allocates a node::Buffer object and initializes it with data backed by the passed in buffer.
/// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. /// While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.
pub fn create_buffer_with_data(&self, mut data: Vec<u8>) -> Result<JsBufferValue> { pub fn create_buffer_with_data(&self, mut data: Vec<u8>) -> Result<JsBufferValue> {
let mut length = data.len(); let length = data.len();
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let data_ptr = data.as_mut_ptr(); let data_ptr = data.as_mut_ptr();
check_status!(unsafe { check_status!(unsafe {
@ -293,7 +293,7 @@ impl Env {
length, length,
data_ptr as *mut c_void, data_ptr as *mut c_void,
Some(drop_buffer), Some(drop_buffer),
&mut length as *mut usize as *mut _, Box::into_raw(Box::new((length, data.capacity()))) as *mut c_void,
&mut raw_value, &mut raw_value,
) )
})?; })?;
@ -403,7 +403,7 @@ impl Env {
#[inline] #[inline]
pub fn create_arraybuffer_with_data(&self, data: Vec<u8>) -> Result<JsArrayBufferValue> { pub fn create_arraybuffer_with_data(&self, data: Vec<u8>) -> Result<JsArrayBufferValue> {
let mut length = data.len(); let length = data.len();
let mut raw_value = ptr::null_mut(); let mut raw_value = ptr::null_mut();
let data_ptr = data.as_ptr(); let data_ptr = data.as_ptr();
check_status!(unsafe { check_status!(unsafe {
@ -412,7 +412,7 @@ impl Env {
data_ptr as *mut c_void, data_ptr as *mut c_void,
length, length,
Some(drop_buffer), Some(drop_buffer),
&mut length as *mut usize as *mut c_void, Box::into_raw(Box::new((length, data.capacity()))) as *mut c_void,
&mut raw_value, &mut raw_value,
) )
})?; })?;
@ -1023,10 +1023,14 @@ impl Env {
} }
} }
unsafe extern "C" fn drop_buffer(env: sys::napi_env, finalize_data: *mut c_void, len: *mut c_void) { unsafe extern "C" fn drop_buffer(
let length = len as *mut u64; env: sys::napi_env,
let length = *length as usize; finalize_data: *mut c_void,
let _ = Vec::from_raw_parts(finalize_data as *mut u8, length, length); hint: *mut c_void,
) {
let length_ptr = hint as *mut (usize, usize);
let (length, cap) = *Box::from_raw(length_ptr);
mem::drop(Vec::from_raw_parts(finalize_data as *mut u8, length, cap));
let mut changed = 0; let mut changed = 0;
let adjust_external_memory_status = let adjust_external_memory_status =
sys::napi_adjust_external_memory(env, -(length as i64), &mut changed); sys::napi_adjust_external_memory(env, -(length as i64), &mut changed);