2017-04-04 96 views
2

我正在使用Nan庫做一個NodeJS插件,我遇到了一個問題,其中調用一個回調(在JavaScript端創建並傳遞給插件被異步執行)會導致段錯誤 - 但只有大約每10萬次運行一次。C++插件Node.js/Nan回調偶然段錯誤

所有事情都是如此複雜,但我希望有人會看到我錯過的東西或能夠弄清楚發生了什麼。

的C++回調函數從JavaScript回調創建這樣的:

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()); 
     } 
    }; 

創建之後,它被傳遞給一個單獨的線程最終使用uv_async_send()發送回調至主libuv線程並執行它。這在絕大多數時間都可以正常工作,但在nodeCb->Call(2, argv)系列上很少出現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 ([email protected]=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() 

編輯:我創建了一個更小的測試程序,看看我是否能找出錯誤的根源,和我我發現我可以通過將Callback shared_ptr更改爲常規指針來防止它,並在nodeCb->Call(2,argv)行之後立即刪除它。

這兩者之間是否存在語義上的差異?

回答

1

shared_ptr的使用包裝一個回調可疑:

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

我不認爲V8想引用被處理的方式。

Nan::Callback本身包含一個持久性,它用來存儲函數的值,所以你不必擔心持久性。

...我剛剛注意到你的編輯後,我已經寫出了這個答案。 shared_ptrV8內部手柄和參考相混合可能會有危險。

如果您的意圖是存儲回調並立即刪除它,也許您可​​以通過重構代碼來使用Nan::AsyncWorker來獲益。