我正在尝试在调用c ++回调时调用已注册的JS函数,但我正在为我认为是一个范围问题而得到一个段错误。
Handle<Value> addEventListener( const Arguments& args ) {
HandleScope scope;
if (!args[0]->IsFunction()) {
return ThrowException(Exception::TypeError(String::New("Wrong arguments")));
}
Persistent<Function> fn = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
Local<Number> num = Number::New(registerListener(&callback, &fn));
scope.Close(num);
}
当事件发生时,将调用以下方法。我假设这可能发生在V8正在执行JS的另一个线程上。
void callback(int event, void* context ) {
HandleScope scope;
Local<Value> args[] = { Local<Value>::New(Number::New(event)) };
Persistent<Function> *func = static_cast<Persistent<Function> *>(context);
(* func)->Call((* func), 1, args);
scope.Close(Undefined());
}
这会导致分段错误:11。请注意,如果我直接使用addEventListener()对Persistent的引用来调用回调函数,它会正确执行该函数。
我假设我需要一个储物柜或隔离物?它看起来像libuv的uv_queue_work()可能能够解决这个问题,但由于我没有启动该线程,我看不出你将如何使用它。
答案 0 :(得分:17)
在代码中声明Persistent<Function> fn
时,fn
是堆栈分配的变量。
fn
是一个Persistent<Function>
,它是一个句柄类,它将包含一个指向某个堆分配值Function
的指针,但是fn
本身就在堆栈中。
这意味着当您调用registerListener(&callback, &fn)
时,&fn
将获取句柄的地址(类型Persistent<Function>
),而不是堆上Function
的地址。当您的函数退出时,句柄将被销毁,但Function
本身将保留在堆上。
所以作为修复,我建议传递Function
的地址而不是句柄的地址,如下所示:
Persistent<Function> fn = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
Local<Number> num = Number::New(registerListener(&callback, *fn));
(请注意operator*
上的Persistent<T>
会返回T*
而不是更传统的T&
,c.f。http://bespin.cz/~ondras/html/classv8_1_1Handle.html)
您还必须调整callback
以说明context
现在是指向Function
的原始指针这一事实,如下所示:
Persistent<Function> func = static_cast<Function*>(context);
func->Call((* func), 1, args);
在这里从原始函数指针创建Persistent<Function>
是可以的,因为我们知道context
实际上是一个持久对象。
为简洁起见,我还将(*func)->Call(...)
更改为func->Call(...)
;他们为V8手柄做同样的事情。
答案 1 :(得分:13)
我知道这个问题有点旧,但是在nodejs v0.10到v0.12中有一个非常重要的更新。 V8改变了v8 :: Persistent的行为。 v8 :: Persistent不再继承自v8 :: Handle。我正在更新一些代码,发现以下工作......
void resize(const v8::FunctionCallbackInfo<Value> &args) {
Isolate *isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Persistent<Function> callback;
callback.Reset(isolate, args[0].As<Function>())
const unsigned argc = 2;
Local<Value> argv[argc] = { Null(isolate), String::NewFromUtf8(isolate, "success") };
Local<Function>::New(isolate, work->callback)->Call(isolate->GetCurrentContext()->Global(), argc, argv);
callback.Reset();
}
我相信此次更新的目标是让暴露内存泄漏变得更加困难。在节点v0.10中,您可能会执行以下操作...
v8::Local<v8::Value> value = /* ... */;
v8::Persistent<v8::Value> persistent = v8::Persistent<v8::Value>::New(value);
// ...
v8::Local<v8::Value> value_again = *persistent;
// ...
persistent.Dispose();
persistent.Clear();
答案 2 :(得分:2)
问题是在addEventListener中,Persistent<Function> fn
在堆栈上分配,然后你将指针用作回调的上下文。
但是,因为fn
已在堆栈上分配,所以当addEventListener
退出时它会消失。因此,回调context
现在指向一些虚假的价值。
您应该分配一些堆空间,并将所需的所有数据放在callback
中。