GeezerVison is showing github.com — node-api: add napi_create_external_sharedarraybuffer · nodejs/node@78c7d77
File treeExpand file treeCollapse file tree Open diff view settings test/node-api/test_buffer
Expand file treeCollapse file tree Open diff view settings Collapse file 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 number | Diff line number | Diff line change |
|---|
@@ -2575,6 +2575,41 @@ object just created has been garbage collected. | 2575 | 2575 | JavaScript `ArrayBuffer`s are described in | 2576 | 2576 | [Section ArrayBuffer objects][] of the ECMAScript Language Specification. | 2577 | 2577 |
| | 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 | + | 2578 | 2613 | #### `napi_create_external_buffer` | 2579 | 2614 |
| 2580 | 2615 | <!-- YAML | |
Collapse file Copy file name to clipboardExpand all lines: src/js_native_api.h +10Lines changed: 10 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -437,6 +437,16 @@ napi_create_external_arraybuffer(napi_env env, | 437 | 437 | node_api_basic_finalize finalize_cb, | 438 | 438 | void* finalize_hint, | 439 | 439 | 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 | 440 | 450 | #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED | 441 | 451 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info( | 442 | 452 | napi_env env, napi_value arraybuffer, void** data, size_t* byte_length); | |
Collapse file Copy file name to clipboardExpand all lines: src/js_native_api_types.h +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -188,6 +188,10 @@ typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env, | 188 | 188 | #endif | 189 | 189 | typedef node_api_nogc_finalize node_api_basic_finalize; | 190 | 190 |
| | 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 | + | 191 | 195 | typedef struct { | 192 | 196 | // One of utf8name or name should be NULL. | 193 | 197 | const char* utf8name; | |
Collapse file Copy file name to clipboardExpand all lines: src/js_native_api_v8.cc +42Lines changed: 42 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -3140,6 +3140,48 @@ napi_create_external_arraybuffer(napi_env env, | 3140 | 3140 | env, buffer, nullptr, nullptr, nullptr, result, nullptr); | 3141 | 3141 | } | 3142 | 3142 |
| | 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 | + | 3143 | 3185 | napi_status NAPI_CDECL napi_get_arraybuffer_info(napi_env env, | 3144 | 3186 | napi_value arraybuffer, | 3145 | 3187 | void** data, | |
Collapse file Copy file name to clipboardExpand all lines: test/node-api/test_buffer/binding.gyp +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
| 3 | 3 | { | 4 | 4 | "target_name": "test_buffer", | 5 | 5 | "defines": [ | 6 | | - 'NAPI_VERSION=10' | | 6 | + "NAPI_EXPERIMENTAL", | | 7 | + "NODE_API_EXPERIMENTAL_NO_WARNING" | 7 | 8 | ], | 8 | 9 | "sources": [ "test_buffer.c" ] | 9 | 10 | }, | |
Collapse file Copy file name to clipboardExpand all lines: test/node-api/test_buffer/test.js +13-1Lines changed: 13 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
| 4 | 4 | const common = require('../../common'); | 5 | 5 | const binding = require(`./build/${common.buildType}/test_buffer`); | 6 | 6 | 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')); | 8 | 9 |
| 9 | 10 | (async function() { | 10 | 11 | assert.strictEqual(binding.newBuffer().toString(), binding.theText); | @@ -26,6 +27,17 @@ const tick = require('util').promisify(require('../../common/tick')); | 26 | 27 | console.log('gc2'); | 27 | 28 | assert.strictEqual(binding.getDeleterCallCount(), 2); | 28 | 29 |
| | 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 | + | 29 | 41 | // To test this doesn't crash | 30 | 42 | binding.invalidObjectAsBuffer({}); | 31 | 43 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/node-api/test_buffer/test_buffer.c +26Lines changed: 26 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -58,6 +58,30 @@ static napi_value newExternalBuffer(napi_env env, napi_callback_info info) { | 58 | 58 | return theBuffer; | 59 | 59 | } | 60 | 60 |
| | 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 | + | 61 | 85 | static napi_value getDeleterCallCount(napi_env env, napi_callback_info info) { | 62 | 86 | napi_value callCount; | 63 | 87 | NODE_API_CALL(env, napi_create_int32(env, deleterCallCount, &callCount)); | @@ -171,6 +195,8 @@ static napi_value Init(napi_env env, napi_value exports) { | 171 | 195 | napi_property_descriptor methods[] = { | 172 | 196 | DECLARE_NODE_API_PROPERTY("newBuffer", newBuffer), | 173 | 197 | DECLARE_NODE_API_PROPERTY("newExternalBuffer", newExternalBuffer), | | 198 | + DECLARE_NODE_API_PROPERTY("newExternalSharedArrayBuffer", | | 199 | + newExternalSharedArrayBuffer), | 174 | 200 | DECLARE_NODE_API_PROPERTY("getDeleterCallCount", getDeleterCallCount), | 175 | 201 | DECLARE_NODE_API_PROPERTY("copyBuffer", copyBuffer), | 176 | 202 | DECLARE_NODE_API_PROPERTY("bufferHasInstance", bufferHasInstance), | |
|