Понимание 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, когда это необходимо?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
1 787
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Для объяснения того, что такое HandleScopes и для чего их использовать, см. Документация V8, например. для класса Local:

There are two types of handles: local and persistent handles.

Local handles are light-weight and transient and typically used in local operations. They are managed by HandleScopes. That means that a HandleScope must exist on the stack when they are created and that they are only valid inside of the HandleScope active during their creation. For passing a local handle to an outer HandleScope, an EscapableHandleScope and its Escape() method must be used.

А для класса HandleScope:

A stack-allocated class that governs a number of local handles. After a handle scope has been created, all local handles will be allocated within that handle scope until either the handle scope is deleted or another handle scope is created. If there is already a handle scope and a new one is created, all allocations will take place in the new handle scope until it is deleted. After that, new handles will again be allocated in the original handle scope.

After the handle scope of a local handle has been deleted the garbage collector will no longer track the object stored in the handle and may deallocate it. The behavior of accessing a handle for which the handle scope has been deleted is undefined.

Прагматически:

  • При вызове из JavaScript в C++ вам понадобится хотя бы один HandleScope, если код C++ создает какие-либо Local<>. Обычно правильный номер - ровно один HandleScope.
  • Создание и уничтожение HandleScopes требует затрат, поэтому, если у вас много детализированных HandleScopes, вы зря теряете время. С другой стороны, HandleScope (по задумке, это его цель!) Поддерживает все объекты (в смысле GC), на которые ссылаются содержащиеся в нем дескрипторы, поэтому для очень длительного кода или циклов с множеством итераций, вы можете захотеть ввести недолговечные HandleScopes, чтобы можно было освободить временные объекты, с которыми вы закончили.
  • Как говорится в документации, вам понадобится EscapableHandleScope, если вы хотите вернуть объект по истечении срока жизни области.

Это неверно. HandleScope обычно не нужен на верхнем уровне метода C++. Процитируем документацию NAPI: «Продолжительность жизни для области по умолчанию привязана к продолжительности жизни вызова собственного метода. В результате, по умолчанию, дескрипторы остаются действительными, а объекты, связанные с этими дескрипторами, будут оставаться живыми в течение всего срока службы. вызов собственного метода ".

Matthijs 03.04.2020 10:37

Это не противоречит тому, что я написал. Вам всегда нужен HandleScope. Если какой-то уровень абстракции (например, NAPI) уже неявно создает один (эта «область по умолчанию», которую вы упомянули), то вам не нужно вручную создавать другой.

jmrk 03.04.2020 11:17

Но вопрос OP был конкретно о Node Addon API и Napi :: HandleScope, а не о V8 и v8 :: HandleScope.

Matthijs 04.04.2020 12:05

Следующее кажется верным для nan, N-API и node-addon-api:

[Handle Scope] is an abstraction used to control and modify the lifetime of objects created within a particular scope. In general, N-API values are created within the context of a handle scope. When a native method is called from JavaScript, a default handle scope will exist. If the user does not explicitly create a new handle scope, N-API values will be created in the default handle scope. For any invocations of code outside the execution of a native method (for instance, during a libuv callback invocation), the module is required to create a scope before invoking any functions that can result in the creation of JavaScript values.

Источник: https://nodejs.org/api/n-api.html#n_api_napi_handle_scope

Это означает, что в приведенных примерах, поскольку оба ожидают const Napi::CallbackInfo& info, очевидно, что оба они вызываются напрямую из JavaScript, поэтому у них уже есть область действия, предоставляемая средой выполнения JS - дополнительные вызовы для создания области необходимы только в том случае, если вы хотите самостоятельно управлять памятью. или в тех случаях, когда ваш код выполняется независимо от JS-движка (например, по таймеру, обратный вызов от чего-то другого, кроме JS-кода и т. д.)

Зачем нам нужно создавать новый HandleScope во фрагменте?

Фактически, мы можем не создавать здесь новый HandleScope. В Node.js есть внешняя область видимости, которая охватывает наши дескрипторы, созданные в этой функции. Но тогда все дескрипторы, созданные в этой функции, будут жить дольше, чем необходимо, и будут обрабатывать затратные ресурсы.

Когда нам понадобится EscapableHandleScope?

Когда дескриптор внутренней области видимости должен жить дольше, чем продолжительность жизни этой области. Например, при возврате данных, вновь созданных в функции.

Ссылка

Встраивание V8: https://v8.dev/docs/embed#handles-and-garbage-collection

узел-аддон-api: https://github.com/nodejs/node-addon-api/blob/master/doc/object_lifetime_management.md

Другие вопросы по теме