在C ++插件中的Node.js / Nancallback中不常发生Segfault

我正在做一个NodeJS插件使用Nan库,我遇到了一个问题,调用一个callback(在JavaScript方面创build,并传递给插件以asynchronous执行)将导致段错误 – 但只有一次10万左右跑。

在所有事情的运作方面都有相当的复杂性,但我希望有人会看到我错过的东西,或者能够弄清楚发生了什么。

C ++callback函数是这样从javascriptcallback中创build的:

auto nodeFunc = val.As<v8::Function>(); auto nodeCb = std::make_shared<Nan::Callback>(nodeFunc); auto callback = [nodeCb] (std::string err, std::string val) -> void { Nan::HandleScope scope; v8::Local<v8::Value> argv[2]; if (err.length() == 0) { auto isolate = v8::Isolate::GetCurrent(); auto json = v8::JSON::Parse(isolate, Nan::New(val).ToLocalChecked()); auto object = json.ToLocalChecked(); argv[0] = Nan::Null(); argv[1] = object; } else { argv[0] = Nan::Error(err.c_str()); argv[1] = Nan::Null(); } try { nodeCb->Call(2, argv); } catch (std::exception& ex) { std::cout << ex.what() << std::endl; Nan::ThrowReferenceError(ex.what()); } }; 

在创build之后,它被传递给一个单独的线程,最终使用uv_async_send()将该callback发送到主libuv线程并执行它。 这在绝大多数情况下都能正常工作,但在nodeCb->Call(2, argv)行上很less发生segfault。

如果有人对这里发生的事有所了解,我真的很感激。

另外,这里是来自gdb的调用堆栈,如果有任何帮助:

 #0 0x00000000009870e0 in v8::Function::Call(v8::Local<v8::Value>, int, v8::Local<v8::Value>*) () #1 0x00000000010a562c in node::MakeCallback(node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*) () #2 0x00000000010a5a98 in node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*) () #3 0x00007ffff47b4b30 in Nan::Callback::Call_ (this=0x20c3500, isolate=0x1ded750, target=..., argc=2, argv=0x7fffffffa430) at ../node_modules/nan/nan.h:1477 #4 0x00007ffff47b4a93 in Nan::Callback::Call (this=0x20c3500, argc=2, argv=0x7fffffffa430) at ../node_modules/nan/nan.h:1443 #5 0x00007ffff47b194b in detail::info::__lambda1::operator() (__closure=0x1e40710, err="", val="{\"index\":1,\"status\":1}") at ../node/utils.hpp:125 #6 0x00007ffff47b37f2 in std::_Function_handler<void(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >), detail::info::setElementValue(T&, v8::Local<v8::Value>, size_t) [with T = std::function<void(std::basic_string<char>, std::basic_string<char>)>; size_t = long unsigned int]::__lambda1>::_M_invoke(const std::_Any_data &, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >) (__functor=..., __args#0="", __args#1="") at /usr/include/c++/4.8.2/functional:2071 #7 0x00007ffff44cd339 in std::function<void (std::string, std::string)>::operator()(std::string, std::string) const (this=0x1e29c80, __args#0="", __args#1="") at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:2271 #8 0x00007ffff44e172c in std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)>::__call<void, , 0ul, 1ul>(std::tuple<>&&, std::_Index_tuple<0ul, 1ul>) (this=0x1e29c80, __args=<unknown type in /usr/local/lib/libSCPlay.so, CU 0x0, DIE 0x83e21>) at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1074 #9 0x00007ffff44daec8 in std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)>::operator()<, void>() (this=0x1e29c80) at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1133 #10 0x00007ffff44d3b58 in std::_Function_handler<void (), std::_Bind<std::function<void (std::string, std::string)> (char const*, std::string)> >::_M_invoke(std::_Any_data const&) (__functor=...) at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:1871 #11 0x00007ffff44fab0a in std::function<void ()>::operator()() const (this=0x7fffffffa650) at /opt/rh/devtoolset-4/root/usr/include/c++/5.2.1/functional:2271 #12 0x00007ffff44f890c in DeviceThread::asyncListener (handle=0x1efb9f0) at /home/scl37510/Projects/SCPlay2/lib/device_thread.cpp:124 #13 0x0000000001316b0b in uv__async_event (loop=0x1de7fe0 <default_loop_struct>, w=<optimized out>, nevents=<optimized out>) at ../deps/uv/src/unix/async.c:98 #14 0x0000000001316be3 in uv__async_io (loop=0x1de7fe0 <default_loop_struct>, w=0x1de81a8 <default_loop_struct+456>, events=<optimized out>) at ../deps/uv/src/unix/async.c:138 #15 0x00000000013271b0 in uv__io_poll (loop=loop@entry=0x1de7fe0 <default_loop_struct>, timeout=0) at ../deps/uv/src/unix/linux-core.c:380 #16 0x00000000013176c6 in uv_run (loop=0x1de7fe0 <default_loop_struct>, mode=UV_RUN_ONCE) at ../deps/uv/src/unix/core.c:354 #17 0x00000000010aabe0 in node::Start(int, char**) () #18 0x00007ffff6bf5b35 in __libc_start_main () from /lib64/libc.so.6 #19 0x00000000007b1f1d in _start () 

编辑:我创build了一个更小的testing程序,以查看是否可以查明错误的来源,我发现可以通过将Callback shared_ptr更改为常规指针来防止它,在nodeCb->Call(2,argv)线。

这两者之间是否有语义上的差异?

使用shared_ptr来包装callback是可疑的:

 std::make_shared<Nan::Callback>(nodeFunc); 

我不认为V8会像这样处理引用。

Nan::Callback本身包含一个Persistent,它用来存储函数的值,所以你不用担心持久性。

…我刚刚注意到你的编辑后,我已经写出了这个答案。 shared_ptrV8内部句柄和引用混合可能是危险的。

如果你的意图是存储callback,并立即删除,也许你可以通过重构代码来使用Nan :: AsyncWorker 。