node-api: add napi_create_external_sharedarraybuffer · nodejs/node@66de634 · GitHub
Skip to content

Commit 66de634

Browse files
bnoordhuisjuanarbol
authored andcommitted
node-api: add napi_create_external_sharedarraybuffer
Creates a SharedArrayBuffer from externally managed memory. Fixes: #62259 PR-URL: #62623 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Vladimir Morozov <vmorozov@microsoft.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Signed-off-by: Juan José Arboleda <soyjuanarbol@gmail.com>
1 parent e306521 commit 66de634

7 files changed

Lines changed: 132 additions & 2 deletions

File tree

‎doc/api/n-api.md‎

Lines changed: 35 additions & 0 deletions

‎src/js_native_api.h‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,16 @@ napi_create_external_arraybuffer(napi_env env,
420420
node_api_basic_finalize finalize_cb,
421421
void* finalize_hint,
422422
napi_value* result);
423+
#ifdef NAPI_EXPERIMENTAL
424+
#define NODE_API_EXPERIMENTAL_HAS_CREATE_EXTERNAL_SHAREDARRAYBUFFER
425+
NAPI_EXTERN napi_status NAPI_CDECL
426+
node_api_create_external_sharedarraybuffer(napi_env env,
427+
void* external_data,
428+
size_t byte_length,
429+
node_api_noenv_finalize finalize_cb,
430+
void* finalize_hint,
431+
napi_value* result);
432+
#endif // NAPI_EXPERIMENTAL
423433
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
424434
NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info(
425435
napi_env env, napi_value arraybuffer, void** data, size_t* byte_length);

‎src/js_native_api_types.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env,
174174
#endif
175175
typedef node_api_nogc_finalize node_api_basic_finalize;
176176

177+
// A finalizer that can be called from any thread and at any time.
178+
typedef void(NAPI_CDECL* node_api_noenv_finalize)(void* finalize_data,
179+
void* finalize_hint);
180+
177181
typedef struct {
178182
// One of utf8name or name should be NULL.
179183
const char* utf8name;

‎src/js_native_api_v8.cc‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3068,6 +3068,48 @@ napi_create_external_arraybuffer(napi_env env,
30683068
env, buffer, nullptr, nullptr, nullptr, result, nullptr);
30693069
}
30703070

3071+
napi_status NAPI_CDECL
3072+
node_api_create_external_sharedarraybuffer(napi_env env,
3073+
void* external_data,
3074+
size_t byte_length,
3075+
node_api_noenv_finalize finalize_cb,
3076+
void* finalize_hint,
3077+
napi_value* result) {
3078+
NAPI_PREAMBLE(env);
3079+
CHECK_ARG(env, result);
3080+
#ifdef V8_ENABLE_SANDBOX
3081+
return napi_set_last_error(env, napi_no_external_buffers_allowed);
3082+
#else
3083+
struct FinalizerData {
3084+
void (*cb)(void* external_data, void* finalize_hint);
3085+
void* hint;
3086+
};
3087+
auto deleter = [](void* external_data, size_t length, void* deleter_data) {
3088+
if (auto fd = static_cast<FinalizerData*>(deleter_data)) {
3089+
fd->cb(external_data, fd->hint);
3090+
delete fd;
3091+
}
3092+
};
3093+
FinalizerData* deleter_data = nullptr;
3094+
if (finalize_cb != nullptr) {
3095+
deleter_data = new FinalizerData{finalize_cb, finalize_hint};
3096+
}
3097+
auto unique_backing_store = v8::SharedArrayBuffer::NewBackingStore(
3098+
external_data,
3099+
byte_length,
3100+
deleter,
3101+
reinterpret_cast<void*>(deleter_data));
3102+
CHECK(!!unique_backing_store); // Cannot fail.
3103+
auto shared_backing_store =
3104+
std::shared_ptr<v8::BackingStore>(std::move(unique_backing_store));
3105+
auto shared_array_buffer =
3106+
v8::SharedArrayBuffer::New(env->isolate, std::move(shared_backing_store));
3107+
CHECK_MAYBE_EMPTY(env, shared_array_buffer, napi_generic_failure);
3108+
*result = v8impl::JsValueFromV8LocalValue(shared_array_buffer);
3109+
return napi_clear_last_error(env);
3110+
#endif // V8_ENABLE_SANDBOX
3111+
}
3112+
30713113
napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
30723114
napi_value arraybuffer,
30733115
void** data,

‎test/node-api/test_buffer/binding.gyp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
{
44
"target_name": "test_buffer",
55
"defines": [
6-
'NAPI_VERSION=10'
6+
"NAPI_EXPERIMENTAL",
7+
"NODE_API_EXPERIMENTAL_NO_WARNING"
78
],
89
"sources": [ "test_buffer.c" ]
910
},

‎test/node-api/test_buffer/test.js‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
const common = require('../../common');
55
const binding = require(`./build/${common.buildType}/test_buffer`);
66
const assert = require('assert');
7-
const tick = require('util').promisify(require('../../common/tick'));
7+
const util = require('util');
8+
const tick = util.promisify(require('../../common/tick'));
89

910
(async function() {
1011
assert.strictEqual(binding.newBuffer().toString(), binding.theText);
@@ -26,6 +27,17 @@ const tick = require('util').promisify(require('../../common/tick'));
2627
console.log('gc2');
2728
assert.strictEqual(binding.getDeleterCallCount(), 2);
2829

30+
// Caveat emptor: it's indeterminate when the SharedArrayBuffer's backing
31+
// store is reclaimed; at least some of the time it happens even before
32+
// calling gc().
33+
let sab = binding.newExternalSharedArrayBuffer();
34+
assert(util.types.isSharedArrayBuffer(sab));
35+
sab = null;
36+
global.gc();
37+
await tick(10);
38+
console.log('gc3');
39+
assert.strictEqual(binding.getDeleterCallCount(), 3);
40+
2941
// To test this doesn't crash
3042
binding.invalidObjectAsBuffer({});
3143

‎test/node-api/test_buffer/test_buffer.c‎

Lines changed: 26 additions & 0 deletions

0 commit comments

Comments
 (0)