diff --git a/README.md b/README.md index fb6308ad7..c31922b40 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ values. Concepts and operations generally map to ideas specified in the - [ArrayBuffer](doc/array_buffer.md) - [TypedArray](doc/typed_array.md) - [TypedArrayOf](doc/typed_array_of.md) + - [Memory Management](doc/memory_management.md) - [Async Operations](doc/async_operations.md) - [AsyncWorker](doc/async_worker.md) - [Promises](doc/promises.md) diff --git a/doc/memory_management.md b/doc/memory_management.md new file mode 100644 index 000000000..2cabd57ca --- /dev/null +++ b/doc/memory_management.md @@ -0,0 +1,27 @@ +# MemoryManagement + +The `MemoryManagement` class contains functions that give the JavaScript engine +an indication of the amount of externally allocated memory that is kept alive by +JavaScript objects. + +## Methods + +### AdjustExternalMemory + +The function `AdjustExternalMemory` adjusts the amount of registered external +memory used to give the JavaScript engine an indication of the amount of externally +allocated memory that is kept alive by JavaScript objects. +The JavaScript engine uses this to decide when to perform global garbage collections. +Registering externally allocated memory will trigger global garbage collections +more often than it would otherwise in an attempt to garbage collect the JavaScript +objects that keep the externally allocated memory alive. + +```cpp +static int64_t MemoryManagement::AdjustExternalMemory(Env env, int64_t change_in_bytes); +``` + +- `[in] env`: The environment in which the API is invoked under. +- `[in] change_in_bytes`: The change in externally allocated memory that is kept +alive by JavaScript objects expressed in bytes. + +Returns the adjusted memory value. diff --git a/napi-inl.h b/napi-inl.h index 77e4d5584..46ba2c0b9 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3224,6 +3224,17 @@ inline void AsyncWorker::OnWorkComplete( delete self; } +//////////////////////////////////////////////////////////////////////////////// +// Memory Management class +//////////////////////////////////////////////////////////////////////////////// + +inline int64_t MemoryManagement::AdjustExternalMemory(Env env, int64_t change_in_bytes) { + int64_t result; + napi_status status = napi_adjust_external_memory(env, change_in_bytes, &result); + NAPI_THROW_IF_FAILED(env, status, 0); + return result; +} + // These macros shouldn't be useful in user code. #undef NAPI_THROW #undef NAPI_THROW_IF_FAILED diff --git a/napi.h b/napi.h index 0aa9a6af1..f9852968b 100644 --- a/napi.h +++ b/napi.h @@ -76,6 +76,8 @@ namespace Napi { /// Defines the signature of a N-API C++ module's registration callback (init) function. typedef Object (*ModuleRegisterCallback)(Env env, Object exports); + class MemoryManagement; + /// Environment for N-API values and operations. /// /// All N-API values and operations must be associated with an environment. An environment @@ -1549,6 +1551,12 @@ namespace Napi { std::string _error; }; + // Memory management. + class MemoryManagement { + public: + static int64_t AdjustExternalMemory(Env env, int64_t change_in_bytes); + }; + } // namespace Napi // Inline implementations of all the above class methods are included here. diff --git a/package.json b/package.json index 352cfc382..f1fdeb73a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "Anna Henningsen (https://github.com/addaleax)", "Arunesh Chandra (https://github.com/aruneshchandra)", "Benjamin Byholm (https://github.com/kkoopa)", - "Cory Mickelson (https://github.com/corymickelson)", + "Cory Mickelson (https://github.com/corymickelson)", "David Halls (https://github.com/davedoesdev)", "Eric Bickle (https://github.com/ebickle)", "Gabriel Schulhof (https://github.com/gabrielschulhof)", diff --git a/test/binding.cc b/test/binding.cc index 8e6a1e9ec..0032c8e30 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -13,6 +13,7 @@ Object InitError(Env env); Object InitExternal(Env env); Object InitFunction(Env env); Object InitHandleScope(Env env); +Object InitMemoryManagement(Env env); Object InitName(Env env); Object InitObject(Env env); Object InitPromise(Env env); @@ -33,6 +34,7 @@ Object Init(Env env, Object exports) { exports.Set("function", InitFunction(env)); exports.Set("name", InitName(env)); exports.Set("handlescope", InitHandleScope(env)); + exports.Set("memory_management", InitMemoryManagement(env)); exports.Set("object", InitObject(env)); exports.Set("promise", InitPromise(env)); exports.Set("typedarray", InitTypedArray(env)); diff --git a/test/binding.gyp b/test/binding.gyp index 4e0050480..acbbc0a12 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -13,6 +13,7 @@ 'external.cc', 'function.cc', 'handlescope.cc', + 'memory_management.cc', 'name.cc', 'object/delete_property.cc', 'object/get_property.cc', diff --git a/test/index.js b/test/index.js index 5211bb467..c9d7fb0c2 100644 --- a/test/index.js +++ b/test/index.js @@ -19,6 +19,7 @@ let testModules = [ 'external', 'function', 'handlescope', + 'memory_management', 'name', 'object/delete_property', 'object/get_property', diff --git a/test/memory_management.cc b/test/memory_management.cc new file mode 100644 index 000000000..a42357539 --- /dev/null +++ b/test/memory_management.cc @@ -0,0 +1,17 @@ +#include "napi.h" + +using namespace Napi; + +Value externalAllocatedMemory(const CallbackInfo& info) { + int64_t kSize = 1024 * 1024; + int64_t baseline = MemoryManagement::AdjustExternalMemory(info.Env(), 0); + int64_t tmp = MemoryManagement::AdjustExternalMemory(info.Env(), kSize); + tmp = MemoryManagement::AdjustExternalMemory(info.Env(), -kSize); + return Boolean::New(info.Env(), tmp == baseline); +} + +Object InitMemoryManagement(Env env) { + Object exports = Object::New(env); + exports["externalAllocatedMemory"] = Function::New(env, externalAllocatedMemory); + return exports; +} diff --git a/test/memory_management.js b/test/memory_management.js new file mode 100644 index 000000000..f4911a2a6 --- /dev/null +++ b/test/memory_management.js @@ -0,0 +1,10 @@ +'use strict'; +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`./build/${buildType}/binding.node`)); +test(require(`./build/${buildType}/binding_noexcept.node`)); + +function test(binding) { + assert.strictEqual(binding.memory_management.externalAllocatedMemory(), true) +}