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ハンドルクラスであり、ヒープに割り当てられた型の値へのポインタが含まれますがPersistent<Function>、それ自体はスタック上にあります。Functionfn

これは、を呼び出すとき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));

(リターンでは、従来の方法ではなく、http://bespin.cz/~ondras/html/classv8_1_1Handle.htmlを参照operator*してください)Persistent<T>T*T&

また、次のように、がへの生のポインタになっているcallbackという事実を考慮して調整する必要があります。contextFunction

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

問題は、addEventListenerPersistent<Function> fnがスタックに割り当てられ、それへのポインタをコールバックのコンテキストとして使用していることです。

しかし、fnスタック上に確保されているため、addEventListener抜けると消えてしまいます。したがって、コールバックを使用contextすると、偽の値がポイントされます。

ヒープ領域を割り当て、必要なすべてのデータをそこに配置する必要がcallbackあります。

于 2012-12-16T04:10:55.407 に答える