n-api: keep napi_env alive while it has finalizers

Manage the napi_env refcount from Finalizer instances, as the
finalizer may refer to the napi_env until it is deleted.

Fixes: https://github.com/nodejs/node/issues/31134
Fixes: https://github.com/node-ffi-napi/node-ffi-napi/issues/48
PR-URL: https://github.com/nodejs/node/pull/31140
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
Anna Henningsen committed 2020-01-04 07:38:36 +01:00
1 parent d845915d46
commit 493faf606d
3 files changed
+39 -6

No files matched your search

+23 -5
View File
@@ -186,26 +186,43 @@ inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
// Adapter for napi_finalize callbacks.
class Finalizer {
public:
// Some Finalizers are run during shutdown when the napi_env is destroyed,
// and some need to keep an explicit reference to the napi_env because they
// are run independently.
enum EnvReferenceMode {
kNoEnvReference,
kKeepEnvReference
};
protected:
Finalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
void* finalize_hint,
EnvReferenceMode refmode = kNoEnvReference)
: _env(env),
_finalize_callback(finalize_callback),
_finalize_data(finalize_data),
_finalize_hint(finalize_hint) {
_finalize_hint(finalize_hint),
_has_env_reference(refmode == kKeepEnvReference) {
if (_has_env_reference)
_env->Ref();
}
~Finalizer() = default;
~Finalizer() {
if (_has_env_reference)
_env->Unref();
}
public:
static Finalizer* New(napi_env env,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr) {
void* finalize_hint = nullptr,
EnvReferenceMode refmode = kNoEnvReference) {
return new Finalizer(
env, finalize_callback, finalize_data, finalize_hint);
env, finalize_callback, finalize_data, finalize_hint, refmode);
}
static void Delete(Finalizer* finalizer) {
@@ -218,6 +235,7 @@ class Finalizer {
void* _finalize_data;
void* _finalize_hint;
bool _finalize_ran = false;
bool _has_env_reference = false;
};
class TryCatch : public v8::TryCatch {
+2 -1
View File
@@ -732,7 +732,8 @@ napi_status napi_create_external_buffer(napi_env env,
// The finalizer object will delete itself after invoking the callback.
v8impl::Finalizer* finalizer = v8impl::Finalizer::New(
env, finalize_cb, nullptr, finalize_hint);
env, finalize_cb, nullptr, finalize_hint,
v8impl::Finalizer::kKeepEnvReference);
auto maybe = node::Buffer::New(isolate,
static_cast<char*>(data),
@@ -0,0 +1,14 @@
'use strict';
const common = require('../../common');
const binding = require(`./build/${common.buildType}/test_buffer`);
const assert = require('assert');
// Regression test for https://github.com/nodejs/node/issues/31134
// Buffers with finalizers are allowed to remain alive until
// Environment cleanup without crashing the process.
// The test stores the buffer on `process` to make sure it lives as long as
// the Context does.
process.externalBuffer = binding.newExternalBuffer();
assert.strictEqual(process.externalBuffer.toString(), binding.theText);