Функция обратного вызова NodeJS Javascript из многопоточного аддона C++

У меня есть многопоточный аддон C++, который выполняет некоторую фоновую обработку, и мне необходимо периодически вызывать его для функции Javascript, которую я написал на своем сервере NodeJS.

Я понимаю, что это включает в себя использование uv_async_send(), так как он должен выполняться в главном потоке, но до сих пор я не смог понять, как это сделать.

Есть ли простой пример, который я пропустил?

1 ответ

Решение

Наконец, это не было слишком сложно, когда я понял, что делают функции uv_*:

1) Предоставьте функцию в аддоне, чтобы позволить узлу установить Cav Javascript, который будет периодически вызываться обратно:

Callback* cbPeriodic; // keep cbPeriodic somewhere
NAN_METHOD(setPeriodicCb) {
    cbPeriodic = new Callback(info[0].As<Function>());
    //...
}

2) Init UV с uv_async_t экземпляр и функция, которая будет выполняться в главном потоке UV, когда наш рабочий поток вызывает uv_async_send()

uv_async_t async; // keep this instance around for as long as we might need to do the periodic callback
uv_loop_t* loop = uv_default_loop();
uv_async_init(loop, &async, asyncmsg);

void asyncmsg(uv_async_t* handle) {
  // Called by UV in main thread after our worker thread calls uv_async_send()
  //    I.e. it's safe to callback to the CB we defined in node!
  Nan::HandleScope scope;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  Local<Value> argv[] = { v8::String::NewFromUtf8(isolate, "Hello world") };
  cbPeriodic->Call(1, argv);
}

3) Позвонить uv_async_send из рабочего потока, передавая наш асинхронный экземпляр выше, всякий раз, когда нам нужно сделать периодический обратный вызов

uv_async_send(&async);

4) Наконец, когда нам больше не нужно выполнять обратный вызов, очистите вещи:

uv_close((uv_handle_t*) &async, NULL);

Приложение:

С тех пор как я написал этот ответ, я столкнулся с другими проблемами и, наконец, усвоил некоторые уроки о libuv. Для полноты вы должны понимать:

  • Помимо uv_async_send все функции libuv могут вызываться только из потока основного цикла! (Я видел, что упоминалось, что другие не были потокобезопасными, но это слишком слабое утверждение.) Даже, например, uv_async_init а также uv_close должен быть вызван из основного потока цикла.

  • Если твой uv_async_t Экземпляр распределяется динамически, обратите внимание, что вы не можете освобождать память до тех пор, пока uv_close не сделает обратный вызов, чтобы вы знали, что это безопасно.

То есть:

auto async = new uv_async_t();
...
uv_close((uv_handle_t*)async, [](uv_handle_t* handle) {
    delete handle;
});
Другие вопросы по тегам