2015-07-03 87 views
3

我正在編寫一個Node插件,我在嘗試從C++工作線程調用V8函數對象時遇到問題。V8多線程功能

我的插件基本上啓動了一個C++ std ::線程,並使用WaitForSingleOject()進入一個等待循環,這是由一個不同的C++應用程序(一個X平面插件)寫入一些共享內存觸發的。我試圖讓我的Node插件在Windows共享事件發出信號時喚醒,然後調用我從節點應用程序註冊的JavaScript函數,該函數又將源自X-Plane的數據傳遞迴節點和網絡世界。

我已經設法解決了如何註冊JavaScript函數並從C++調用它,但只能在主V8線程中調用它。我似乎無法找到從std :: thread調用函數的方法。我試過了各種方法,更衣室對象(變量成功),持久性功能(沒有工作),保存主隔離對象,輸入/退出隔離,但如果/當代碼最終到達函數對象這是無效的。

我得到了不同的結果,從崩潰到凍結,取決於我是否創建各種更衣櫃和解鎖器對象。

我完全是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

你可能需要一點魔法libuv拿到的node.js/V8線程從另一個線程執行回調函數。這將涉及:

  • A uv_async_t handle充當了警鐘主V8螺紋:

    extern uv_async_t  async; 
    
  • 一個uv_async_init調用,它結合了uv_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通過一些數據++插件線程節點/ V8:

    extern mutex     zqueue_mutex; 
    extern std::queue<NotifInfo *> zqueue; 
    
  • 當事情發生在你的C++線程中,只需將一個新項目推送到受互斥鎖保護的隊列中,然後調用uv_async_send喚醒V8的默認事件循環(即所謂的「主線程」)來處理該項目(然後可以調用您的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); 
    } 
    

(從the official Node.JS addon for OpenZWave採取代碼段)

+0

感謝,如果我有機會,我會嘗試了這一點。我將在此期間將其標記爲答案。 – TheZapper