mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
node-api: add napi_create_external_sharedarraybuffer
Creates a SharedArrayBuffer from externally managed memory. Fixes: https://github.com/nodejs/node/issues/62259 PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
1 parent
e9e73f56fa
commit
0e83bae7cb
7 files changed
+132
-2
No files matched your search
@@ -2565,6 +2565,41 @@ object just created has been garbage collected.
|
||||
JavaScript `ArrayBuffer`s are described in
|
||||
[Section ArrayBuffer objects][] of the ECMAScript Language Specification.
|
||||
|
||||
#### `node_api_create_external_sharedarraybuffer`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
```c
|
||||
napi_status
|
||||
node_api_create_external_sharedarraybuffer(napi_env env,
|
||||
void* external_data,
|
||||
size_t byte_length,
|
||||
node_api_noenv_finalize finalize_cb,
|
||||
void* finalize_hint,
|
||||
napi_value* result)
|
||||
```
|
||||
|
||||
* `[in] env`: The environment that the API is invoked under.
|
||||
* `[in] external_data`: Pointer to the underlying byte buffer of the
|
||||
`SharedArrayBuffer`.
|
||||
* `[in] byte_length`: The length in bytes of the underlying buffer.
|
||||
* `[in] finalize_cb`: Optional callback to call when the `SharedArrayBuffer` is
|
||||
being collected. Called on an arbitrary thread. Because a `SharedArrayBuffer`
|
||||
can outlive the environment it's created in, the callback does not receive a
|
||||
reference to `env`.
|
||||
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
|
||||
collection.
|
||||
* `[out] result`: A `napi_value` representing a JavaScript `SharedArrayBuffer`.
|
||||
|
||||
Returns `napi_ok` if the API succeeded.
|
||||
|
||||
Create a `SharedArrayBuffer` with externally managed memory.
|
||||
|
||||
See the entry on [`napi_create_external_arraybuffer`][] for runtime
|
||||
compatibility.
|
||||
|
||||
#### `napi_create_external_buffer`
|
||||
|
||||
<!-- YAML
|
||||
|
||||
@@ -420,6 +420,16 @@ napi_create_external_arraybuffer(napi_env env,
|
||||
node_api_basic_finalize finalize_cb,
|
||||
void* finalize_hint,
|
||||
napi_value* result);
|
||||
#ifdef NAPI_EXPERIMENTAL
|
||||
#define NODE_API_EXPERIMENTAL_HAS_CREATE_EXTERNAL_SHAREDARRAYBUFFER
|
||||
NAPI_EXTERN napi_status NAPI_CDECL
|
||||
node_api_create_external_sharedarraybuffer(napi_env env,
|
||||
void* external_data,
|
||||
size_t byte_length,
|
||||
node_api_noenv_finalize finalize_cb,
|
||||
void* finalize_hint,
|
||||
napi_value* result);
|
||||
#endif // NAPI_EXPERIMENTAL
|
||||
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
|
||||
NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info(
|
||||
napi_env env, napi_value arraybuffer, void** data, size_t* byte_length);
|
||||
|
||||
@@ -174,6 +174,10 @@ typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env,
|
||||
#endif
|
||||
typedef node_api_nogc_finalize node_api_basic_finalize;
|
||||
|
||||
// A finalizer that can be called from any thread and at any time.
|
||||
typedef void(NAPI_CDECL* node_api_noenv_finalize)(void* finalize_data,
|
||||
void* finalize_hint);
|
||||
|
||||
typedef struct {
|
||||
// One of utf8name or name should be NULL.
|
||||
const char* utf8name;
|
||||
|
||||
@@ -3068,6 +3068,48 @@ napi_create_external_arraybuffer(napi_env env,
|
||||
env, buffer, nullptr, nullptr, nullptr, result, nullptr);
|
||||
}
|
||||
|
||||
napi_status NAPI_CDECL
|
||||
node_api_create_external_sharedarraybuffer(napi_env env,
|
||||
void* external_data,
|
||||
size_t byte_length,
|
||||
node_api_noenv_finalize finalize_cb,
|
||||
void* finalize_hint,
|
||||
napi_value* result) {
|
||||
NAPI_PREAMBLE(env);
|
||||
CHECK_ARG(env, result);
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
return napi_set_last_error(env, napi_no_external_buffers_allowed);
|
||||
#else
|
||||
struct FinalizerData {
|
||||
void (*cb)(void* external_data, void* finalize_hint);
|
||||
void* hint;
|
||||
};
|
||||
auto deleter = [](void* external_data, size_t length, void* deleter_data) {
|
||||
if (auto fd = static_cast<FinalizerData*>(deleter_data)) {
|
||||
fd->cb(external_data, fd->hint);
|
||||
delete fd;
|
||||
}
|
||||
};
|
||||
FinalizerData* deleter_data = nullptr;
|
||||
if (finalize_cb != nullptr) {
|
||||
deleter_data = new FinalizerData{finalize_cb, finalize_hint};
|
||||
}
|
||||
auto unique_backing_store = v8::SharedArrayBuffer::NewBackingStore(
|
||||
external_data,
|
||||
byte_length,
|
||||
deleter,
|
||||
reinterpret_cast<void*>(deleter_data));
|
||||
CHECK(!!unique_backing_store); // Cannot fail.
|
||||
auto shared_backing_store =
|
||||
std::shared_ptr<v8::BackingStore>(std::move(unique_backing_store));
|
||||
auto shared_array_buffer =
|
||||
v8::SharedArrayBuffer::New(env->isolate, std::move(shared_backing_store));
|
||||
CHECK_MAYBE_EMPTY(env, shared_array_buffer, napi_generic_failure);
|
||||
*result = v8impl::JsValueFromV8LocalValue(shared_array_buffer);
|
||||
return napi_clear_last_error(env);
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
}
|
||||
|
||||
napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env,
|
||||
napi_value arraybuffer,
|
||||
void** data,
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
{
|
||||
"target_name": "test_buffer",
|
||||
"defines": [
|
||||
'NAPI_VERSION=10'
|
||||
"NAPI_EXPERIMENTAL",
|
||||
"NODE_API_EXPERIMENTAL_NO_WARNING"
|
||||
],
|
||||
"sources": [ "test_buffer.c" ]
|
||||
},
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
const common = require('../../common');
|
||||
const binding = require(`./build/${common.buildType}/test_buffer`);
|
||||
const assert = require('assert');
|
||||
const tick = require('util').promisify(require('../../common/tick'));
|
||||
const util = require('util');
|
||||
const tick = util.promisify(require('../../common/tick'));
|
||||
|
||||
(async function() {
|
||||
assert.strictEqual(binding.newBuffer().toString(), binding.theText);
|
||||
@@ -26,6 +27,17 @@ const tick = require('util').promisify(require('../../common/tick'));
|
||||
console.log('gc2');
|
||||
assert.strictEqual(binding.getDeleterCallCount(), 2);
|
||||
|
||||
// Caveat emptor: it's indeterminate when the SharedArrayBuffer's backing
|
||||
// store is reclaimed; at least some of the time it happens even before
|
||||
// calling gc().
|
||||
let sab = binding.newExternalSharedArrayBuffer();
|
||||
assert(util.types.isSharedArrayBuffer(sab));
|
||||
sab = null;
|
||||
global.gc();
|
||||
await tick(10);
|
||||
console.log('gc3');
|
||||
assert.strictEqual(binding.getDeleterCallCount(), 3);
|
||||
|
||||
// To test this doesn't crash
|
||||
binding.invalidObjectAsBuffer({});
|
||||
|
||||
|
||||
@@ -58,6 +58,30 @@ static napi_value newExternalBuffer(napi_env env, napi_callback_info info) {
|
||||
return theBuffer;
|
||||
}
|
||||
|
||||
static char externalSharedArrayBufferData[1];
|
||||
|
||||
static void freeExternalSharedArrayBuffer(void* data, void* hint) {
|
||||
(void)hint;
|
||||
NODE_API_BASIC_ASSERT_RETURN_VOID(
|
||||
data == (void*)externalSharedArrayBufferData,
|
||||
"SharedArrayBuffer points to wrong data");
|
||||
deleterCallCount++;
|
||||
}
|
||||
|
||||
static napi_value newExternalSharedArrayBuffer(napi_env env,
|
||||
napi_callback_info info) {
|
||||
napi_value sab;
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
node_api_create_external_sharedarraybuffer(env,
|
||||
externalSharedArrayBufferData,
|
||||
1,
|
||||
freeExternalSharedArrayBuffer,
|
||||
NULL,
|
||||
&sab));
|
||||
return sab;
|
||||
}
|
||||
|
||||
static napi_value getDeleterCallCount(napi_env env, napi_callback_info info) {
|
||||
napi_value callCount;
|
||||
NODE_API_CALL(env, napi_create_int32(env, deleterCallCount, &callCount));
|
||||
@@ -171,6 +195,8 @@ static napi_value Init(napi_env env, napi_value exports) {
|
||||
napi_property_descriptor methods[] = {
|
||||
DECLARE_NODE_API_PROPERTY("newBuffer", newBuffer),
|
||||
DECLARE_NODE_API_PROPERTY("newExternalBuffer", newExternalBuffer),
|
||||
DECLARE_NODE_API_PROPERTY("newExternalSharedArrayBuffer",
|
||||
newExternalSharedArrayBuffer),
|
||||
DECLARE_NODE_API_PROPERTY("getDeleterCallCount", getDeleterCallCount),
|
||||
DECLARE_NODE_API_PROPERTY("copyBuffer", copyBuffer),
|
||||
DECLARE_NODE_API_PROPERTY("bufferHasInstance", bufferHasInstance),
|
||||
|
||||
Reference in new issue
Block a user