Понимание Node Addon API (N-API) HandleScope
У меня есть трудности, чтобы понять, как правильно использовать HandleScope и EscapableHandleScope. Например, из этого примера узла:
MyObject::MyObject(const Napi::CallbackInfo& info) : Napi::ObjectWrap<MyObject>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
this->val_ = info[0].As<Napi::Number>().DoubleValue();
};
Зачем нам нужно создавать новый HandleScope в этом случае? И из этого другого примера:
Napi::Object CreateObject(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::Object obj = Napi::Object::New(env);
obj.Set(Napi::String::New(env, "msg"), info[0].ToString());
return obj;
}
Почему это не нужно здесь?
Кроме того, я не нашел ни одного примера использования EscapableHandleScope, когда это нужно?
1 ответ
Похоже, что для nan, N-API и node-addon-api справедливо следующее:
[Handle Scope] - это абстракция, используемая для управления и изменения времени жизни объектов, созданных в определенной области. Как правило, значения N-API создаются в контексте области дескриптора. Когда собственный метод вызывается из JavaScript, будет существовать область дескриптора по умолчанию. Если пользователь явно не создает новую область дескриптора, значения N-API будут созданы в области дескриптора по умолчанию. Для любых вызовов кода вне выполнения собственного метода (например, во время вызова обратного вызова libuv) модуль должен создать область действия перед вызовом любых функций, которые могут привести к созданию значений JavaScript.
Источник: https://nodejs.org/api/n-api.html
Это означает в приведенных примерах, поскольку оба ожидают const Napi::CallbackInfo& info
очевидно, что оба они вызываются напрямую из JavaScript, поэтому у них уже есть область, предоставляемая средой выполнения JS - дополнительные вызовы для создания области необходимы только в том случае, если вы хотите самостоятельно управлять памятью или в тех случаях, когда ваш код выполняется независимо от движок JS (например, по таймеру, обратный вызов из чего-то другого, кроме кода JS и т. д.)
Зачем нам нужно создавать новый HandleScope в сниппете?
Фактически, мы можем не создавать здесь новый HandleScope. В Node.js есть внешняя область видимости, которая охватывает наши дескрипторы, созданные в этой функции. Но тогда все дескрипторы, созданные в этой функции, будут жить дольше, чем необходимо, и будут обрабатывать затраты ресурсов.
Когда нам понадобится EscapableHandleScope?
Когда дескриптор из внутренней области видимости должен жить дольше, чем срок ее существования. Например, при возврате данных, вновь созданных в функции.
Справка
Встраивание V8: https://v8.dev/docs/embed
узел-аддон-api: https://github.com/nodejs/node-addon-api/blob/master/doc/object_lifetime_management.md
Для объяснения того, что такое HandleScopes и для чего их использовать, см . Документацию V8, например, для класса Local
:
Существует два типа дескрипторов: локальные и постоянные.
Локальные ручки легкие и переходные и обычно используются в локальных операциях. Они управляются HandleScopes. Это означает, что HandleScope должен существовать в стеке при его создании, и что он действителен только внутри HandleScope, активного во время их создания. Для передачи локального дескриптора внешнему HandleScope необходимо использовать EscapableHandleScope и его метод Escape().
И для класса HandleScope
:
Выделенный стеком класс, который управляет несколькими локальными дескрипторами. После того как область дескриптора была создана, все локальные дескрипторы будут размещены в этой области дескриптора, пока либо не будет удалена область дескриптора, либо не будет создана другая область маркера. Если область дескриптора уже существует и создается новая, все выделения будут выполняться в новой области дескриптора, пока она не будет удалена. После этого новые дескрипторы снова будут размещены в исходной области видимости дескрипторов.
После удаления области действия локального дескриптора сборщик мусора больше не будет отслеживать объект, сохраненный в дескрипторе, и может освободить его. Поведение доступа к дескриптору, для которого была удалена область дескриптора, не определено.
С практической точки зрения:
- При звонке из JavaScript в C++ вам понадобится хотя бы один
HandleScope
если код C++ создает какой-либоLocal<>
s. Обычно ровно одинHandleScope
это правильный номер. - Создание и уничтожение HandleScopes обходится дорого, поэтому, если у вас много детальных HandleScopes, вы теряете время. С другой стороны, HandleScope (по замыслу, это его цель!) Поддерживает все объекты живыми (в смысле GC), на которые ссылаются содержащиеся в нем дескрипторы, поэтому для очень долго выполняющегося кода или циклов с большим количеством итераций, Вы можете захотеть ввести недолговечные HandleScopes, чтобы временные объекты, с которыми вы сделали, могли быть освобождены.
- Как сказано в документации, вам нужно
EscapableHandleScope
если вы хотите вернуть объект после окончания срока действия области видимости.