V8多线程功能

The*_*per 3 c++ multithreading v8 node.js node.js-addon

我正在编写一个Node插件,我在尝试从C++工作线程调用V8函数对象时遇到问题.

我的插件基本上启动了一个C++ std :: thread并使用WaitForSingleOject()进入等待循环,这是由写入一些共享内存的不同C++应用程序(一个X-Plane插件)触发的.我试图让我的Node插件在发出Windows共享事件时唤醒,然后调用我从节点应用程序注册的JavaScript函数,该函数将依次将源自X-Plane的数据传递回Node和网络世界.

我已经设法解决了如何注册JavaScript函数并从C++调用它,但仅限于主V8线程.我似乎找不到从std :: thread调用函数的方法.

我尝试了各种方法,Locker对象(变量成功),持久性函数(不起作用),保存主隔离对象,进入/退出隔离,但是如果/当代码最终到达函数对象时它无效.

我得到了不同的结果,从崩溃到冻结,取决于我是否创建了各种储物柜和解锁器对象.

我对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);
}
Run Code Online (Sandbox Code Playgroud)

eka*_*rak 6

你可能需要一些libuv魔法才能让 node.js/v8线程从另一个线程执行你的回调.这将涉及:

  • 一个uv_async_t句柄,用作主v8线程的唤醒调用:

    extern uv_async_t       async;
    
    Run Code Online (Sandbox Code Playgroud)
  • 一个uv_async_init结合了uv_async_t到V8默认循环调用:

    uv_async_init(uv_default_loop(), &async, async_cb_handler);
    
    Run Code Online (Sandbox Code Playgroud)
  • 以及一个事件处理程序,它将作用于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();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 最后,您还可能需要一个受互斥锁保护的队列,以便能够将一些数据从c ++插件线程传递到Node/V8:

    extern mutex                   zqueue_mutex;
    extern std::queue<NotifInfo *> zqueue;
    
    Run Code Online (Sandbox Code Playgroud)
  • 当您的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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

(代码片段取自OpenZWave的官方Node.JS插件)