Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 78c7d77

Browse filesBrowse files
bnoordhuisaduh95
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>
1 parent a0ccf94 commit 78c7d77
Copy full SHA for 78c7d77

7 files changed

+132-2Lines changed: 132 additions & 2 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/n-api.md‎

Copy file name to clipboardExpand all lines: doc/api/n-api.md
+35Lines changed: 35 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,41 @@ object just created has been garbage collected.
25752575
JavaScript `ArrayBuffer`s are described in
25762576
[Section ArrayBuffer objects][] of the ECMAScript Language Specification.
25772577

2578+
#### `node_api_create_external_sharedarraybuffer`
2579+
2580+
<!-- YAML
2581+
added: REPLACEME
2582+
-->
2583+
2584+
```c
2585+
napi_status
2586+
node_api_create_external_sharedarraybuffer(napi_env env,
2587+
void* external_data,
2588+
size_t byte_length,
2589+
node_api_noenv_finalize finalize_cb,
2590+
void* finalize_hint,
2591+
napi_value* result)
2592+
```
2593+
2594+
* `[in] env`: The environment that the API is invoked under.
2595+
* `[in] external_data`: Pointer to the underlying byte buffer of the
2596+
`SharedArrayBuffer`.
2597+
* `[in] byte_length`: The length in bytes of the underlying buffer.
2598+
* `[in] finalize_cb`: Optional callback to call when the `SharedArrayBuffer` is
2599+
being collected. Called on an arbitrary thread. Because a `SharedArrayBuffer`
2600+
can outlive the environment it's created in, the callback does not receive a
2601+
reference to `env`.
2602+
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
2603+
collection.
2604+
* `[out] result`: A `napi_value` representing a JavaScript `SharedArrayBuffer`.
2605+
2606+
Returns `napi_ok` if the API succeeded.
2607+
2608+
Create a `SharedArrayBuffer` with externally managed memory.
2609+
2610+
See the entry on [`napi_create_external_arraybuffer`][] for runtime
2611+
compatibility.
2612+
25782613
#### `napi_create_external_buffer`
25792614

25802615
<!-- YAML
Collapse file

‎src/js_native_api.h‎

Copy file name to clipboardExpand all lines: src/js_native_api.h
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,16 @@ napi_create_external_arraybuffer(napi_env env,
437437
node_api_basic_finalize finalize_cb,
438438
void* finalize_hint,
439439
napi_value* result);
440+
#ifdef NAPI_EXPERIMENTAL
441+
#define NODE_API_EXPERIMENTAL_HAS_CREATE_EXTERNAL_SHAREDARRAYBUFFER
442+
NAPI_EXTERN napi_status NAPI_CDECL
443+
node_api_create_external_sharedarraybuffer(napi_env env,
444+
void* external_data,
445+
size_t byte_length,
446+
node_api_noenv_finalize finalize_cb,
447+
void* finalize_hint,
448+
napi_value* result);
449+
#endif // NAPI_EXPERIMENTAL
440450
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
441451
NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info(
442452
napi_env env, napi_value arraybuffer, void** data, size_t* byte_length);
Collapse file

‎src/js_native_api_types.h‎

Copy file name to clipboardExpand all lines: src/js_native_api_types.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env,
188188
#endif
189189
typedef node_api_nogc_finalize node_api_basic_finalize;
190190

191+
// A finalizer that can be called from any thread and at any time.
192+
typedef void(NAPI_CDECL* node_api_noenv_finalize)(void* finalize_data,
193+
void* finalize_hint);
194+
191195
typedef struct {
192196
// One of utf8name or name should be NULL.
193197
const char* utf8name;
Collapse file

‎src/js_native_api_v8.cc‎

Copy file name to clipboardExpand all lines: src/js_native_api_v8.cc
+42Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3140,6 +3140,48 @@ napi_create_external_arraybuffer(napi_env env,
31403140
env, buffer, nullptr, nullptr, nullptr, result, nullptr);
31413141
}
31423142

3143+
napi_status NAPI_CDECL
3144+
node_api_create_external_sharedarraybuffer(napi_env env,
3145+
void* external_data,
3146+
size_t byte_length,
3147+
node_api_noenv_finalize finalize_cb,
3148+
void* finalize_hint,
3149+
napi_value* result) {
3150+
NAPI_PREAMBLE(env);
3151+
CHECK_ARG(env, result);
3152+
#ifdef V8_ENABLE_SANDBOX
3153+
return napi_set_last_error(env, napi_no_external_buffers_allowed);
3154+
#else
3155+
struct FinalizerData {
3156+
void (*cb)(void* external_data, void* finalize_hint);
3157+
void* hint;
3158+
};
3159+
auto deleter = [](void* external_data, size_t length, void* deleter_data) {
3160+
if (auto fd = static_cast<FinalizerData*>(deleter_data)) {
3161+
fd->cb(external_data, fd->hint);
3162+
delete fd;
3163+
}
3164+
};
3165+
FinalizerData* deleter_data = nullptr;
3166+
if (finalize_cb != nullptr) {
3167+
deleter_data = new FinalizerData{finalize_cb, finalize_hint};
3168+
}
3169+
auto unique_backing_store = v8::SharedArrayBuffer::NewBackingStore(
3170+
external_data,
3171+
byte_length,
3172+
deleter,
3173+
reinterpret_cast<void*>(deleter_data));
3174+
CHECK(!!unique_backing_store); // Cannot fail.
3175+
auto shared_backing_store =
3176+
std::shared_ptr<v8::BackingStore>(std::move(unique_backing_store));
3177+
auto shared_array_buffer =
3178+
v8::SharedArrayBuffer::New(env->isolate, std::move(shared_backing_store));
3179+
CHECK_MAYBE_EMPTY(env, shared_array_buffer, napi_generic_failure);
3180+
*result = v8impl::JsValueFromV8LocalValue(shared_array_buffer);
3181+
return napi_clear_last_error(env);
3182+
#endif // V8_ENABLE_SANDBOX
3183+
}
3184+
31433185
napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
31443186
napi_value arraybuffer,
31453187
void** data,
Collapse file

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

Copy file name to clipboardExpand all lines: test/node-api/test_buffer/binding.gyp
+2-1Lines 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
},
Collapse file

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

Copy file name to clipboardExpand all lines: test/node-api/test_buffer/test.js
+13-1Lines 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

Collapse file

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

Copy file name to clipboardExpand all lines: test/node-api/test_buffer/test_buffer.c
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,30 @@ static napi_value newExternalBuffer(napi_env env, napi_callback_info info) {
5858
return theBuffer;
5959
}
6060

61+
static char externalSharedArrayBufferData[1];
62+
63+
static void freeExternalSharedArrayBuffer(void* data, void* hint) {
64+
(void)hint;
65+
NODE_API_BASIC_ASSERT_RETURN_VOID(
66+
data == (void*)externalSharedArrayBufferData,
67+
"SharedArrayBuffer points to wrong data");
68+
deleterCallCount++;
69+
}
70+
71+
static napi_value newExternalSharedArrayBuffer(napi_env env,
72+
napi_callback_info info) {
73+
napi_value sab;
74+
NODE_API_CALL(
75+
env,
76+
node_api_create_external_sharedarraybuffer(env,
77+
externalSharedArrayBufferData,
78+
1,
79+
freeExternalSharedArrayBuffer,
80+
NULL,
81+
&sab));
82+
return sab;
83+
}
84+
6185
static napi_value getDeleterCallCount(napi_env env, napi_callback_info info) {
6286
napi_value callCount;
6387
NODE_API_CALL(env, napi_create_int32(env, deleterCallCount, &callCount));
@@ -171,6 +195,8 @@ static napi_value Init(napi_env env, napi_value exports) {
171195
napi_property_descriptor methods[] = {
172196
DECLARE_NODE_API_PROPERTY("newBuffer", newBuffer),
173197
DECLARE_NODE_API_PROPERTY("newExternalBuffer", newExternalBuffer),
198+
DECLARE_NODE_API_PROPERTY("newExternalSharedArrayBuffer",
199+
newExternalSharedArrayBuffer),
174200
DECLARE_NODE_API_PROPERTY("getDeleterCallCount", getDeleterCallCount),
175201
DECLARE_NODE_API_PROPERTY("copyBuffer", copyBuffer),
176202
DECLARE_NODE_API_PROPERTY("bufferHasInstance", bufferHasInstance),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.