Блокировка звонков в аддоне Node.js

Я разрабатываю приложение Node.js, которое включает в себя Windows DLL. DLL управляет научным оборудованием, для контекста.

Мой интерфейс от Node к DLL идет хорошо, однако DLL имеет некоторые недетерминированные вызовы, которые зависят от топологии сети и радиосигналов в комнате. Эти звонки могут занять от 10 секунд до 10 минут.

Я хотел бы получить эти вызовы из цикла событий Node и даже избегать AsyncWorkers. Я хотел бы поместить их в свои собственные потоки C++. Я обеспокоен тем, что не знаю достаточно Node/V8, чтобы правильно подойти к проблеме, хотя я пытался дважды.

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

mTp ниже приведен фрагмент реализации пула потоков, который я написал. Runtask принимает лямбда-код C++ в качестве параметра для добавления в мою очередь рабочих потоков. mThreadStatus - это карта из моего дескриптора потока, который является строкой, в перечисление thread_status_t. mThreadResults - это еще одна карта из дескриптора потока в v8::Value, возвращаемая обратным вызовом.

void
MyObj::SpawnThread(functionInput info) {
    MyObj* obj = ObjectWrap::Unwrap<MyObj>(info.Holder());      
    obj->mTp.RunTask([&]() {
        v8::Isolate::CreateParams cp;
        v8::Isolate* tpIsolate = v8::Isolate::New(cp);
        v8::Locker locker(tpIsolate);
        v8::Isolate::Scope isolateScope(tpIsolate);

        Nan::HandleScope scope;

        auto global = obj->mContext.Get(tpIsolate)->Global();

        auto handle = std::string(*v8::String::Utf8Value(info[0]->ToString()));

        {
            std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
            obj->mThreadStatus[handle] = thread_status_t::running;
        }

        v8::Handle<v8::Function> f = v8::Handle<v8::Function>::Cast(info[1]);
        v8::TryCatch trycatch(tpIsolate);
        v8::Handle<v8::Value> result = f->Call(global, 0, nullptr);
        if (result.IsEmpty()) {
            v8::Local<v8::Value> exception = trycatch.Exception();
            std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
            obj->mThreadStatus[handle] = thread_status_t::error;
            return;
        }

        {
            std::unique_lock<std::shared_mutex> resultLock(obj->mThreadResultsMutex);
            obj->mThreadResults[handle] = result;
        }

        std::unique_lock<std::shared_mutex> lock(obj->mThreadStatusMutex);
        obj->mThreadStatus[handle] = completed;

        tpIsolate->Dispose();
    });

Я предполагаю, что мои js выглядят так, чтобы породить поток:

var ctx = this
this.myObj.spawnThread('startMeasurements', () => {
    return ctx.myObj.startMeasurements()
})

И вот так, чтобы получить результат, в моем "демоне":

var status = this.myObj.getThreadStatus('startMeasurements')
if ( status === 'complete') {
    // Publish returned information to front-end
}
else if (status === 'error') {
    // Handle error
}

Кто-нибудь решил эту проблему раньше? Это похоже на достойный подход? Помощь с v8 очень ценится. Спасибо!

1 ответ

Решение

Я не решал подобную проблему раньше, но общий способ, которым я хотел бы пойти по этому поводу:

  • пусть код JavaScript не обращает внимания на потоки
  • выставить функцию getMeasurements(callback) в JavaScript, реализованный в C++
  • когда функция вызывается, она сама получает поток (либо вновь созданный, либо из пула) и инструктирует ее выполнить блокирующий внешний вызов; когда этот вызов завершен, поток сообщает свой результат основному потоку, который вызывает callback с этим.
  • таким образом, все взаимодействие с кодом JavaScript (т. е. все взаимодействие с V8) происходит в основном потоке, и вы используете только фоновые потоки для блокирующих вызовов.

Надеюсь, это поможет!

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