V8 多线程函数
V8 Multithreaded function
我正在编写 Node 插件,但在尝试从 C++ 工作线程调用 V8 函数对象时遇到问题。
我的插件基本上启动 C++ std::thread 并使用 WaitForSingleOject() 进入等待循环,这是由不同的 C++ 应用程序(X-Plane 插件)写入共享内存触发的。我试图让我的节点插件在 Windows 共享事件发出信号时唤醒,然后调用我从节点应用程序注册的 JavaScript 函数,该函数将依次传递数据起源于 X-Plane 回到 Node 和网络世界。
我已经设法弄清楚如何注册一个 JavaScript 函数并从 C++ 调用它,但只能在 V8 主线程中调用。我似乎找不到从 std::thread 调用函数的方法。
我尝试了各种方法,Locker 对象(变量成功),持久函数(没用),保存主隔离对象,entering/exiting 隔离,但是 if/when 代码最终到达无效的函数对象。
我得到不同的结果,从崩溃到冻结,具体取决于我是否创建了各种储物柜和解锁器对象。
我是 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);
}
您可能需要一点 libuv
魔法才能获得 main 节点。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++ 插件线程传递到 Node/V8:
extern mutex zqueue_mutex;
extern std::queue<NotifInfo *> zqueue;
当您的 C++ 线程 发生某些事情时,只需将一个新项目推送到互斥保护队列,然后调用 uv_async_send
唤醒V8 的默认事件循环(所谓的 'main thread')来处理项目(然后可以调用您的 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 的代码片段)
我正在编写 Node 插件,但在尝试从 C++ 工作线程调用 V8 函数对象时遇到问题。
我的插件基本上启动 C++ std::thread 并使用 WaitForSingleOject() 进入等待循环,这是由不同的 C++ 应用程序(X-Plane 插件)写入共享内存触发的。我试图让我的节点插件在 Windows 共享事件发出信号时唤醒,然后调用我从节点应用程序注册的 JavaScript 函数,该函数将依次传递数据起源于 X-Plane 回到 Node 和网络世界。
我已经设法弄清楚如何注册一个 JavaScript 函数并从 C++ 调用它,但只能在 V8 主线程中调用。我似乎找不到从 std::thread 调用函数的方法。
我尝试了各种方法,Locker 对象(变量成功),持久函数(没用),保存主隔离对象,entering/exiting 隔离,但是 if/when 代码最终到达无效的函数对象。
我得到不同的结果,从崩溃到冻结,具体取决于我是否创建了各种储物柜和解锁器对象。
我是 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);
}
您可能需要一点 libuv
魔法才能获得 main 节点。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++ 插件线程传递到 Node/V8:
extern mutex zqueue_mutex; extern std::queue<NotifInfo *> zqueue;
当您的 C++ 线程 发生某些事情时,只需将一个新项目推送到互斥保护队列,然后调用
uv_async_send
唤醒V8 的默认事件循环(所谓的 'main thread')来处理项目(然后可以调用您的 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 的代码片段)