2012-12-11 9 views
13

Ich versuche, eine registrierte JS-Funktion aufzurufen, wenn ein C++ - Callback aufgerufen wird, aber ich bekomme einen segfault für das, was ich vermute, ist ein Problembereich.Aufruf der Javascript-Funktion von einem C++ - Callback in V8

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

Wenn ein Ereignis eintritt, wird die folgende Methode aufgerufen. Ich nehme an, dass dies wahrscheinlich in einem anderen Thread geschieht, in dem V8 JS ausführt.

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

Dies verursacht eine Segmentation fault: 11. Man beachte, dass, wenn ich die Callback-Funktion direkt mit einem Verweis auf Persistent von addEventListener() aufrufen, es die Funktion richtig ausgeführt wird.

Ich gehe davon aus, dass ich ein Schließfach oder Isolieren brauche? Es sieht auch so aus, als könnte libuv's uv_queue_work() das lösen, aber da ich den Thread nicht starte, kann ich nicht sehen, wie du ihn benutzen würdest.

Antwort

17

Wenn Sie in Ihrem Code Persistent<Function> fn deklarieren, ist fn eine Variable, die dem Stack zugewiesen wurde.

fn ist ein Persistent<Function>, das ein Griff Klasse, und es wird ein Zeiger zu einem gewissen heap zugewiesenen Wert vom Typ Function, enthalten aber fn sich auf dem Stapel befindet.

Das bedeutet, dass, wenn Sie registerListener(&callback, &fn) nennen, &fn die Adresse des Griffs nimmt (Typ Persistent<Function>), nicht die Adresse des Function auf dem Heap. Wenn Ihre Funktion beendet wird, wird der Handle zerstört, aber die Function selbst bleibt auf dem Heap.

So als fix, schlage ich vor, das Bestehen der Adresse des Function anstelle der Adresse des Griffs, wie folgt aus:

Persistent<Function> fn = Persistent<Function>::New(Handle<Function>::Cast(args[0])); 
Local<Number> num = Number::New(registerListener(&callback, *fn)); 

(beachten Sie, dass operator* auf einem Persistent<T> gibt ein T* eher als die konventionelleren T&, cf http://bespin.cz/~ondras/html/classv8_1_1Handle.html)

Sie werden auch callback anpassen müssen für die Tatsache zu berücksichtigen, dass context zu einem Function jetzt ein Rohzeiger ist, wie folgt aus:

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

ein Persistent<Function> aus einem rohen Funktionszeiger hier zu schaffen, ist in Ordnung, weil wir wissen, dass context tatsächlich ein persistentes Objekt ist.

Ich habe auch (*func)->Call(...) zu func->Call(...) der Kürze wegen geändert; Sie tun das gleiche für V8-Griffe.

+0

Danke, das vereinfacht den Code und behebt das Problem mit dem Bereich, aber ich hatte auf einige Informationen gehofft wie aus dem Callback-Thread zum Haupt-Thread zurückgerufen wird. Ich habe dies mit der Funktion eio_nop() aus der EIO-Bibliothek erreicht, aber der bevorzugte Weg ist die Verwendung von libuv. Mein Problem ist, dass es kein libuv-Äquivalent von eio_nop zu geben scheint. – marchaos

+1

@marchaos Ok. Ich war nicht ganz klar, was Sie auf der Threading-Seite wollten. Wie ich es verstehe, was Sie suchen, ist in der Lage, JS aus dem Callback im Hauptkontext des v8-Threads auszuführen. Ich habe eine kleine Demo zusammengestellt, wie man das mit Isolaten/Schließfächern macht (https://gist.github.com/4341994). Beachten Sie, dass dies bedeutet, dass Sie überall einstellen müssen, wo Sie V8 verwenden, um das Isolat zu sperren, bevor Sie etwas anderes tun! – je4d

+0

Danke. Ich werde es versuchen, aber es sieht nach dem richtigen Ansatz aus. – marchaos

2

Das Problem ist, dass in addEventListener, Persistent<Function> fn auf dem Stapel zugeordnet ist, und dann nehmen Sie den Zeiger zu diesem als Kontext für den Rückruf verwenden.

Aber, weil fn auf dem Stapel zugeordnet ist, verschwindet es, wenn addEventListener beendet wird. Also mit dem Rückruf context zeigen Sie jetzt auf einen falschen Wert.

Sie sollten einige Heap-Speicherplatz zuweisen, und legen Sie alle erforderlichen Daten in callback dort.

+1

ich intern glauben V8 Zuweisungen etwas Persistent auf dem Heap - es ist sicherlich da zu sein entworfen, bis Sie ausdrücklich entsorgen. – marchaos

+0

bestätigen. "Persistente Handles bieten einen Verweis auf ein Heap-allokiertes JavaScript-Objekt" von https://developers.google.com/v8/embed –

13

Ich weiß, dass diese Frage ein wenig alt ist, aber es gab ein ziemlich großes Update in nodejs v0.10 zu v0.12. V8 hat das Verhalten von v8 :: Persistent geändert. v8 :: Persistent erbt nicht mehr von v8 :: Handle. Ich war einigen Code zu aktualisieren und festgestellt, dass die folgenden gearbeitet ...

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

Ich glaube, das Ziel dieses Update machen sollte es schwieriger Speicherlecks zu belichten. In Knoten v0.10, würden Sie so etwas wie die folgenden getan haben ...

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(); 
Verwandte Themen