как реализовать обратный вызов node-nan с помощью node-addon-api

До сих пор я реализовывал только синхронные методы node-addon-api, то есть функция JavaScript выполняет вызов, работа выполняется, и аддон возвращается. У меня большие пробелы в знаниях о внутренней работе v8, libuv и node, поэтому, пожалуйста, исправьте все очевидные заблуждения.

Цель состоит в том, чтобы вызвать обратный вызов JavaScript, когда обратные вызовы сборки мусора C++ вызываются из v8. Первоначально я просто вызвал обратный вызов JavaScript из обратного вызова сборки мусора v8, но после пары вызовов это закончилось segv. Кажется, что просто выполнение вызова в JavaScript при вызове из обратного вызова v8 имеет некоторые проблемы (v8 docs, обратные вызовы не должны выделять объекты). Итак, я огляделся и нашел пример на основе Nan, который используетlibuv и Нэн AsyncResourceчтобы сделать обратный звонок. Следующий подход работает с использованиемnode-nan:

NAN_GC_CALLBACK(afterGC) {
    uint64_t et = uv_hrtime() - gcStartTime;

    // other bookkeeping for GCData_t raw.

    if (doCallbacks) {
        uv_async_t* async = new uv_async_t;
        GCData_t* data = new GCData_t;

        *data = raw;
        data->gcTime = et;

        async->data = data;

        uv_async_init(uv_default_loop(), async, asyncCB);
        uv_async_send(async);
    }
}

class GCResponseResource : public Nan::AsyncResource {
 public:
    GCResponseResource(Local<Function> callback_)
        : Nan::AsyncResource("nan:gcstats.DeferredCallback") {
    callback.Reset(callback_);
    }

    ~GCResponseResource() {
        callback.Reset();
    }

    Nan::Persistent<Function> callback;
};

static GCResponseResource* asyncResource;

static void closeCB(uv_handle_t *handle) {
    delete handle;
}

static void asyncCB(uv_async_t *handle) {
    Nan::HandleScope scope;
    GCData_t* data = static_cast<GCData_t*>(handle->data);

    Local<Object> obj = Nan::New<Object>();

    Nan::Set(obj, Nan::New("gcCount").ToLocalChecked(),
        Nan::New<Number>((data->gcCount));
    Nan::Set(obj, Nan::New("gcTime").ToLocalChecked(),
        Nan::New<Number>(data->gcTime));

    Local<Object> counts = Nan::New<v8::Object>();
    for (int i = 0; i < maxTypeCount; i++) {
        if (data->typeCounts[i] != 0) {
            Nan::Set(counts, i, Nan::New<Number>(data->typeCounts[i]));
        }
    }
    Nan::Set(obj, Nan::New("gcTypeCounts").ToLocalChecked(), counts);

    Local<Value> arguments[] = {obj};
    Local<Function> callback = Nan::New(asyncResource->callback);
    v8::Local<v8::Object> target = Nan::New<v8::Object>();
    asyncResource->runInAsyncScope(target, callback, 1, arguments);
    delete data;
    uv_close((uv_handle_t*) handle, closeCB);
}

У меня вопрос: как мне это сделать, используя node-addon-api вместо nan?

Мне не ясно, какой эквивалент node-addon-api uv_async_init, uv_async_sendи т. д. есть. Частично это потому, что мне непонятно, что лежит в основеN-API (в отличие от node-addon-api) требуются функции.

Мне не удалось найти подобный пример. Пример обратного вызова полностью синхронно. В примере async pi для выполнения задачи используется рабочий поток, но это кажется излишним по сравнению с подходом в коде на основе nan с использованиемuv примитивы.

1 ответ

Ваш пример на самом деле не асинхронный, потому что обратные вызовы GC выполняются в основном потоке. Однако, когда мир JS останавливается из-за GC, это не означает, что он останавливается таким образом, чтобы разрешить выполнение обратного вызова, поскольку GC может остановить его в середине функции.

Ты нуждаешься в ThreadSafeFunctionсделать это. Посмотрите здесь пример:https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md

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