13

我试图在调用 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() 也可以解决这个问题,但是由于我没有启动线程,所以我看不出你会如何使用它。

4

3 回答 3

17

当您Persistent<Function> fn在代码中声明时,fn是一个堆栈分配的变量。

fn是 a 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*在 aPersistent<T>返回 aT*而不是更传统的T&,参见http://bespin.cz/~ondras/html/classv8_1_1Handle.html

您还必须进行调整callback以考虑到context现在是指向 a 的原始指针的事实Function,如下所示:

Persistent<Function> func = static_cast<Function*>(context);
func->Call((* func), 1, args);

在这里从原始函数指针创建一个Persistent<Function>是可以的,因为我们知道它context实际上是一个持久对象。

为简洁起见,我也改为(*func)->Call(...)func->Call(...);他们对 V8 手柄做同样的事情。

于 2012-12-16T19:23:06.933 回答
14

我知道这个问题有点老了,但是 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();
于 2015-02-17T03:17:08.910 回答
2

问题是在 addEventListener 中,Persistent<Function> fn分配在堆栈上,然后您将指向它的指针用作回调的上下文。

但是,因为是在栈上分配的,所以退出fn时就消失了。addEventListener因此,现在回调context指向一些虚假值。

您应该分配一些堆空间,并将您需要的所有数据放在callback那里。

于 2012-12-16T04:10:55.407 回答