4

Node プラグインを作成していますが、C++ ワーカー スレッドから V8 関数オブジェクトを呼び出そうとすると問題が発生します。

私のプラグインは基本的に C++ std::thread を開始し、WaitForSingleOject() を使用して待機ループに入ります。これは、共有メモリのビットに書き込む別の C++ アプリ (X-Plane プラグイン) によってトリガーされます。Windows共有イベントが通知されたときにNodeプラグインを起動させ、ノードアプリから登録したJavaScript関数を呼び出そうとしています。これにより、X-Planeで発生したデータがNodeに戻されますそしてウェブの世界。

JavaScript 関数を登録して C++ から呼び出す方法をなんとか見つけましたが、メインの V8 スレッドでのみです。std::thread から関数を呼び出す方法が見つからないようです。

さまざまなアプローチ、Locker オブジェクト (変数の成功)、Persistent 関数 (機能しませんでした)、メインの分離オブジェクトの保存、分離の開始/終了などを試しましたが、コードが最終的に関数オブジェクトに到達した場合は有効ではありません。

さまざまなロッカーおよびロック解除オブジェクトを作成するかどうかに応じて、クラッシュからフリーズまで、さまざまな結果が得られます。

私は V8 にまったく慣れていないので、自分が正しいことをしているのかよくわかりません。問題のコードは次のとおりです。

誰かが助けてくれたら、私は永遠に感謝します!.

float* mem = 0;
HANDLE event = NULL;
Isolate* thisIsolate;

void readSharedMemory()
{
    //Isolate* isolate = Isolate::GetCurrent();
    //HandleScope scope(isolate);

    thisIsolate->Enter();
    v8::Locker locker(thisIsolate);
    v8::Isolate::Scope isolateScope(thisIsolate);
    //HandleScope scope(thisIsolate);        

    //v8::Local<Value> myVal = v8::String::NewFromUtf8(isolate, "Plugin world");
    v8::Local<Value> myVal = v8::Number::New(thisIsolate, *mem);

    // If it get's this far 'myFunction' is not valid
    bool isFun = myFunction->IsFunction();
    isFun = callbackFunction->IsFunction();

    v8::Context *thisContext = *(thisIsolate->GetCurrentContext());
    myFunction->Call(thisContext->Global(), 1, &(Handle<Value>(myVal)));
}

void registerCallback(const FunctionCallbackInfo<Value>& args)
{
    Isolate* isolate = Isolate::GetCurrent();
    v8::Locker locker(isolate);
    HandleScope scope(isolate);

    /** Standard parameter checking code removed **/

    // Various attempts at saving a function object
    v8::Local<v8::Value> func = args[0];
    bool isFun = func->IsFunction();

    Handle<Object> callbackObject = args[0]->ToObject();

    callbackFunction = Handle<Function>::Cast(callbackObject);
    isFun = callbackFunction->IsFunction();

    // save the function call object - This appears to work
    myFunction = v8::Function::Cast(*callbackObject);
    isFun = myFunction->IsFunction();


    // Test the function - this works *without* the Unlocker object below
    v8::Local<Value> myVal = v8::String::NewFromUtf8(isolate, "Plugin world");   
    myFunction->Call(isolate->GetCurrentContext()->Global(), 1, &(Handle<Value>(myVal)));
}

void threadFunc()
{
    thisIsolate->Exit();
    // If I include this unlocker, the function call test above fails.
    // If I don't include it, the app hangs trying to create the locker in 'readSharedMemory()'
    //v8::Unlocker unlocker(thisIsolate); 

    event = OpenEventW(EVENT_ALL_ACCESS, FALSE, L"Global\\myEventObject");
    DWORD err = GetLastError();

    //thisIsolate = v8::Isolate::New();

    std::cout << "Hello from thread" << std::endl;
    bool runThread = true;

    while (runThread)
    {
        DWORD dwWaitResult;
        DWORD waitTime = 60000;
        dwWaitResult = WaitForSingleObject(event, waitTime);

        err = GetLastError();

        if (dwWaitResult == WAIT_TIMEOUT)
            runThread = false;

        // event has been signaled - continue
        readSharedMemory();    
    }
}

void init(Handle<Object> exports) 
{
    /** NODE INITILISATION STUFF REMOVED **/

    // save the isolate - Is this a safe thing to do?
    thisIsolate = Isolate::GetCurrent();
    //Launch a thread
    eventThread = std::thread(threadFunc);
}
4

1 に答える 1

7

メインのnode.js/v8 スレッドを取得して、別のlibuvスレッドからコールバックを実行するには、おそらく少し魔法が必要です。これには以下が含まれます。

  • メイン v8 スレッドのウェイクアップ コールとして機能する uv_async_t ハンドル:

    extern uv_async_t       async;
    
  • uv_async_inituv_async_t を V8 デフォルト ループにバインドする呼び出し:

    uv_async_init(uv_default_loop(), &async, async_cb_handler);
    
  • そして、 v8 メイン スレッドの uvasync_t イベントに作用するイベント ハンドラー:

    void async_cb_handler(uv_async_t *handle) {
        NotifInfo *notif;
        mutex::scoped_lock sl(zqueue_mutex);
        while (!zqueue.empty()) {
            notif = zqueue.front();
            handleNotification(notif);
            delete notif;
            zqueue.pop();
        }
    }
    
  • 最後に、c++ アドオン スレッドから Node/V8 にデータを渡すことができるようにするには、mutex で保護されたキューも必要になるでしょう。

    extern mutex                   zqueue_mutex;
    extern std::queue<NotifInfo *> zqueue;
    
  • C++ スレッドで何かが発生した場合、新しいアイテムをミューテックスで保護されたキューにプッシュし、uv_async_sendV8 のデフォルト イベント ループ (いわゆる「メイン スレッド」) を呼び出してアイテムを処理します (その後、Javascript コールバックを呼び出すことができます)。 )

    void ozw_watcher_callback(OpenZWave::Notification const *cb, void *ctx) {
        NotifInfo *notif = new NotifInfo();
        notif->type   = cb->GetType();
        notif->homeid = cb->GetHomeId();
        ...
        mutex::scoped_lock sl(zqueue_mutex);
        zqueue.push(notif);
        uv_async_send(&async);
    }
    

( OpenZWave の公式 Node.JS アドオンから取得したコード スニペット)

于 2015-12-21T16:52:54.703 に答える