Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(napi): should also delete the reference while dropping the Buffer #1331

Merged
merged 4 commits into from Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/msrv.yml
Expand Up @@ -63,8 +63,3 @@ jobs:
yarn test --verbose
env:
RUST_BACKTRACE: 1

- name: Clear the cargo caches
run: |
cargo install cargo-cache --no-default-features --features ci-autoclean
cargo-cache
5 changes: 5 additions & 0 deletions crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs
Expand Up @@ -67,6 +67,11 @@ macro_rules! impl_typed_array {
crate::check_status_or_throw!(
env,
unsafe { sys::napi_reference_unref(env, ref_, &mut 0) },
"Failed to unref Buffer reference in drop"
);
crate::check_status_or_throw!(
env,
unsafe { sys::napi_delete_reference(env, ref_) },
"Failed to delete Buffer reference in drop"
);
return;
Expand Down
64 changes: 33 additions & 31 deletions crates/napi/src/bindgen_runtime/js_values/buffer.rs
Expand Up @@ -25,23 +25,27 @@ pub struct Buffer {
pub(crate) len: usize,
pub(crate) capacity: usize,
raw: Option<(sys::napi_ref, sys::napi_env)>,
// use it as ref count
pub(crate) drop_in_vm: Arc<()>,
pub(crate) ref_count: Arc<()>,
}

impl Drop for Buffer {
fn drop(&mut self) {
if let Some((ref_, env)) = self.raw {
check_status_or_throw!(
env,
unsafe { sys::napi_reference_unref(env, ref_, &mut 0) },
"Failed to unref Buffer reference in drop"
);
return;
}

if Arc::strong_count(&self.drop_in_vm) == 1 {
unsafe { Vec::from_raw_parts(self.inner.as_ptr(), self.len, self.capacity) };
if Arc::strong_count(&self.ref_count) == 1 {
if let Some((ref_, env)) = self.raw {
let mut ref_count = 0;
check_status_or_throw!(
env,
unsafe { sys::napi_reference_unref(env, ref_, &mut ref_count) },
"Failed to unref Buffer reference in drop"
);
check_status_or_throw!(
env,
unsafe { sys::napi_delete_reference(env, ref_) },
"Failed to delete Buffer reference in drop"
);
} else {
unsafe { Vec::from_raw_parts(self.inner.as_ptr(), self.len, self.capacity) };
}
}
}
}
Expand All @@ -50,24 +54,15 @@ impl Drop for Buffer {
// without synchronization. Also see the docs for the `AsMut` impl.
unsafe impl Send for Buffer {}

impl Buffer {
pub fn clone(&mut self, env: &Env) -> Result<Self> {
let raw = if let Some((ref_, _)) = self.raw {
check_status!(
unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) },
"Failed to ref Buffer reference in Buffer::clone"
)?;
Some((ref_, env.0))
} else {
None
};
Ok(Self {
impl Clone for Buffer {
fn clone(&self) -> Self {
Self {
inner: self.inner,
len: self.len,
capacity: self.capacity,
raw,
drop_in_vm: self.drop_in_vm.clone(),
})
raw: self.raw,
ref_count: self.ref_count.clone(),
}
}
}

Expand Down Expand Up @@ -95,7 +90,7 @@ impl From<Vec<u8>> for Buffer {
len,
capacity,
raw: None,
drop_in_vm: Arc::new(()),
ref_count: Arc::new(()),
}
}
}
Expand Down Expand Up @@ -184,7 +179,7 @@ impl FromNapiValue for Buffer {
len,
capacity: len,
raw: Some((ref_, env)),
drop_in_vm: Arc::new(()),
ref_count: Arc::new(()),
})
}
}
Expand Down Expand Up @@ -227,9 +222,16 @@ impl ToNapiValue for Buffer {
}
}

impl ToNapiValue for &Buffer {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
let buf = val.clone();
unsafe { ToNapiValue::to_napi_value(env, buf) }
}
}

impl ToNapiValue for &mut Buffer {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
let buf = val.clone(&Env::from(env))?;
let buf = val.clone();
unsafe { ToNapiValue::to_napi_value(env, buf) }
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/napi/src/bindgen_runtime/mod.rs
Expand Up @@ -73,7 +73,7 @@ pub unsafe extern "C" fn raw_finalize_unchecked<T: ObjectFinalize>(
#[doc(hidden)]
pub unsafe extern "C" fn drop_buffer(
_env: sys::napi_env,
finalize_data: *mut c_void,
#[allow(unused)] finalize_data: *mut c_void,
finalize_hint: *mut c_void,
) {
#[cfg(debug_assertions)]
Expand Down
6 changes: 2 additions & 4 deletions examples/napi/src/typed_array.rs
Expand Up @@ -68,8 +68,6 @@ impl Task for AsyncBuffer {
}

#[napi]
fn async_reduce_buffer(mut buf: Buffer, env: Env) -> Result<AsyncTask<AsyncBuffer>> {
Ok(AsyncTask::new(AsyncBuffer {
buf: buf.clone(&env)?,
}))
fn async_reduce_buffer(buf: Buffer) -> Result<AsyncTask<AsyncBuffer>> {
Ok(AsyncTask::new(AsyncBuffer { buf: buf.clone() }))
}
12 changes: 7 additions & 5 deletions memory-testing/buffer.mjs
Expand Up @@ -10,16 +10,18 @@ const require = createRequire(import.meta.url)
const api = require(`./index.node`)

let i = 1
const FIXTURE = Buffer.allocUnsafe(1000 * 1000 * 20)
// eslint-disable-next-line no-constant-condition
while (true) {
api.bufferLen()
api.arrayBufferLen()
api.bufferConvert(Buffer.from(Array.from({ length: 1024 * 10240 }).fill(1)))
api.arrayBufferConvert(
Uint8Array.from(Array.from({ length: 1024 * 10240 }).fill(1)),
)
api.bufferConvert(Buffer.from(FIXTURE))
api.arrayBufferConvert(Uint8Array.from(FIXTURE))
api.bufferPassThrough(Buffer.from(FIXTURE))
api.arrayBufferPassThrough(Uint8Array.from(FIXTURE))
if (i % 10 === 0) {
await setTimeout(100)
await setTimeout(1000)
global?.gc?.()
displayMemoryUsageFromNode(initialMemoryUsage)
}
i++
Expand Down
10 changes: 10 additions & 0 deletions memory-testing/src/lib.rs
Expand Up @@ -161,3 +161,13 @@ pub fn array_buffer_convert(array_buffer: Uint8Array) -> Uint8Array {
pub fn array_buffer_len() -> u32 {
Uint8Array::new(vec![1; 1024 * 10240]).len() as u32
}

#[napi]
pub fn buffer_pass_through(buffer: Buffer) -> Buffer {
buffer
}

#[napi]
pub fn array_buffer_pass_through(array_buffer: Uint8Array) -> Uint8Array {
array_buffer
}
10 changes: 7 additions & 3 deletions memory-testing/test-util.mjs
Expand Up @@ -10,7 +10,7 @@ const sleep = promisify(setTimeout)
const client = new Dockerode()

export async function createSuite(testFile, maxMemoryUsage) {
console.info(chalk.cyanBright('Create container'))
console.info(chalk.cyanBright(`Create container to test ${testFile}`))

const container = await client.createContainer({
Image: 'node:lts-slim',
Expand Down Expand Up @@ -74,6 +74,10 @@ export async function createSuite(testFile, maxMemoryUsage) {

shouldAssertMemoryUsage = true

await container.stop()
await container.remove()
try {
await container.stop()
await container.remove()
} catch (e) {
console.error(e)
}
}