尝试使用 C++ 插件调用节点 js 回调时崩溃
Crash when trying to call node js callback using C++ addon
我正在尝试从节点 js 调用一些本机 api 并执行长 运行 任务。我为此使用 libuv,存储 js 回调以在单独的线程中执行一些有意义的任务并稍后调用回调。这是我到目前为止所做的:
task.cpp
struct Work {
uv_work_t request;
Local<Function> callback;
};
static void WorkAsyncComplete(uv_work_t* req, int status)
{
Isolate* isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work* work = static_cast<Work*>(req->data);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "Hello world").ToLocalChecked() };
Local<Function> callback = Local<Function>::New(isolate, work->callback);
callback->Call(isolate->GetCurrentContext(), Null(isolate), argc, argv);
work->callback.Clear();
delete work;
}
static void WorkAsync(uv_work_t* req)
{
Work* work = static_cast<Work*>(req->data);
using namespace std::chrono_literals;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
void RunCallback(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
Work* work = new Work();
work->request.data = work;
Local<Function> callback = Local<Function>::Cast(args[1]);
work->callback.New(isolate, callback);
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void Init(Local<Object> exports, Local<Object> module)
{
//isolate = exports->GetIsolate();
NODE_SET_METHOD(module, "exports", RunCallback);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
task.js:
let task = require('./build/Release/task');
task((msg) => {
console.log(msg);
}
但是我在调用回调时在 WorkAsyncComplete() 中崩溃了。这是错误的详细信息:
Debugger attached.
FATAL ERROR: v8::Function::Call Function to be called is a null pointer
1: 00007FF6579E03DF napi_wrap+109311
2: 00007FF657985096 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfElementsOffset+33302
3: 00007FF657985E66 node::OnFatalError+294
4: 00007FF65822A9BD v8::Function::Call+573
5: 00007FFA62FD1172 load_exe_hook+258
6: 00007FF657A37D90 uv_timer_stop+560
7: 00007FF657A37E67 uv_timer_stop+775
8: 00007FF657A3469B uv_async_send+331
9: 00007FF657A33E2C uv_loop_init+1292
10: 00007FF657A33FCA uv_run+202
11: 00007FF6579400A5 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfBucketsOffset+9365
12: 00007FF6579B3867 node::Start+311
13: 00007FF65781686C RC4_options+339820
14: 00007FF6587B523C v8::internal::compiler::RepresentationChanger::Uint32OverflowOperatorFor+153532
15: 00007FFA761A7C24 BaseThreadInitThunk+20
16: 00007FFA7694D4D1 RtlUserThreadStart+33
请告知如何解决此问题。提前致谢。
看来问题是您在长期对象中使用了 v8::Local<...>
。这是行不通的,因为 Local
的生命周期与周围的 HandleScope
相关联:当 HandleScope
死亡时,Local
变得无效。有关详细信息,请参阅 https://v8.dev/docs/embed#handles-and-garbage-collection。解决方案是使用 v8::Persistent<...>
而不是 Local
.
郑重声明,我还想指出,虽然您可以在后台任务中执行 C++ 工作,但您无法扩展此示例,因此它需要一个 JavaScript 函数并发执行。那将是多线程编程,JavaScript 作为一种语言不支持,因此 V8 作为一种实现也不支持。
我正在尝试从节点 js 调用一些本机 api 并执行长 运行 任务。我为此使用 libuv,存储 js 回调以在单独的线程中执行一些有意义的任务并稍后调用回调。这是我到目前为止所做的:
task.cpp
struct Work {
uv_work_t request;
Local<Function> callback;
};
static void WorkAsyncComplete(uv_work_t* req, int status)
{
Isolate* isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work* work = static_cast<Work*>(req->data);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "Hello world").ToLocalChecked() };
Local<Function> callback = Local<Function>::New(isolate, work->callback);
callback->Call(isolate->GetCurrentContext(), Null(isolate), argc, argv);
work->callback.Clear();
delete work;
}
static void WorkAsync(uv_work_t* req)
{
Work* work = static_cast<Work*>(req->data);
using namespace std::chrono_literals;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
void RunCallback(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
Work* work = new Work();
work->request.data = work;
Local<Function> callback = Local<Function>::Cast(args[1]);
work->callback.New(isolate, callback);
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void Init(Local<Object> exports, Local<Object> module)
{
//isolate = exports->GetIsolate();
NODE_SET_METHOD(module, "exports", RunCallback);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
task.js:
let task = require('./build/Release/task');
task((msg) => {
console.log(msg);
}
但是我在调用回调时在 WorkAsyncComplete() 中崩溃了。这是错误的详细信息:
Debugger attached.
FATAL ERROR: v8::Function::Call Function to be called is a null pointer
1: 00007FF6579E03DF napi_wrap+109311
2: 00007FF657985096 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfElementsOffset+33302
3: 00007FF657985E66 node::OnFatalError+294
4: 00007FF65822A9BD v8::Function::Call+573
5: 00007FFA62FD1172 load_exe_hook+258
6: 00007FF657A37D90 uv_timer_stop+560
7: 00007FF657A37E67 uv_timer_stop+775
8: 00007FF657A3469B uv_async_send+331
9: 00007FF657A33E2C uv_loop_init+1292
10: 00007FF657A33FCA uv_run+202
11: 00007FF6579400A5 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfBucketsOffset+9365
12: 00007FF6579B3867 node::Start+311
13: 00007FF65781686C RC4_options+339820
14: 00007FF6587B523C v8::internal::compiler::RepresentationChanger::Uint32OverflowOperatorFor+153532
15: 00007FFA761A7C24 BaseThreadInitThunk+20
16: 00007FFA7694D4D1 RtlUserThreadStart+33
请告知如何解决此问题。提前致谢。
看来问题是您在长期对象中使用了 v8::Local<...>
。这是行不通的,因为 Local
的生命周期与周围的 HandleScope
相关联:当 HandleScope
死亡时,Local
变得无效。有关详细信息,请参阅 https://v8.dev/docs/embed#handles-and-garbage-collection。解决方案是使用 v8::Persistent<...>
而不是 Local
.
郑重声明,我还想指出,虽然您可以在后台任务中执行 C++ 工作,但您无法扩展此示例,因此它需要一个 JavaScript 函数并发执行。那将是多线程编程,JavaScript 作为一种语言不支持,因此 V8 作为一种实现也不支持。