2012-12-11 11 views
13

Próbuję wywołać zarejestrowaną funkcję JS, gdy wywoływany jest wywołanie zwrotne C++, ale otrzymuję błąd segfault, co do którego zakładam, że jest problemem z zakresu.Wywołanie funkcji JavaScript z wywołania zwrotnego C++ w 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); 
} 

Po wystąpieniu zdarzenia wywoływana jest następująca metoda. Zakładam, że prawdopodobnie dzieje się to w innym wątku, do którego V8 wykonuje 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()); 
} 

Powoduje to winy segmentacji: 11. Zauważ, że jeśli wywołanie funkcji oddzwaniania bezpośrednio z odniesieniem do Persistent od addEventListener(), to wykonuje funkcję prawidłowo.

Zakładam, że potrzebuję szafki lub izolatki? Wygląda też na to, że uv_queue_work() libuv może to rozwiązać, ale ponieważ nie zaczynam wątku, nie widzę, jak byś go użył.

Odpowiedz

17

Po zadeklarowaniu w swoim kodzie Persistent<Function> fn, fn jest zmienną przypisaną do stosu.

fn jest Persistent<Function>, który jest uchwyt klasy i będzie zawierać wskaźnik do jakiejś sterty przydzielone wartości typu Function, ale fn sam w sobie jest na stosie.

To oznacza, że ​​podczas rozmowy registerListener(&callback, &fn), &fn bierze adres uchwytem (typ Persistent<Function>), a nie adres Function na stercie. Kiedy twoja funkcja zostanie zakończona, uchwyt zostanie zniszczony, ale sam obiekt pozostanie na stercie.

Więc jak poprawki, proponuję przepuszczenie adres Function zamiast adresu rączki, tak:

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

(zauważ, że operator* na Persistent<T> zwraca T* raczej niż bardziej konwencjonalny T&, cf http://bespin.cz/~ondras/html/classv8_1_1Handle.html)

będziesz także musiał dostosować callback w celu uwzględnienia faktu, że context obecnie jest surowy wskaźnik do Function, tak:

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

Tworzenie Persistent<Function> z surowego wskaźnik funkcji tutaj jest OK, bo wiemy, że context jest rzeczywiście trwały obiekt.

Zmieniłem także (*func)->Call(...) na func->Call(...) dla zwięzłości; robią to samo dla uchwytów V8.

+0

Dzięki temu upraszcza to kod i rozwiązuje problem z zakresu, ale miałem nadzieję na pewne informacje jako jak oddzwonić do głównego wątku z wątku wywołania zwrotnego. Osiągnąłem to za pomocą funkcji eio_nop() z biblioteki EIO, ale preferowanym sposobem jest użycie libuv. Mój problem polega na tym, że nie wydaje się, aby libuv był równy eio_nop. – marchaos

+1

@marchaos Ok. Nie do końca wiedziałem, o co prosisz po stronie wątków. Jak rozumiem, to, czego szukasz, jest w stanie wykonać JS z wywołania zwrotnego w kontekście wątku głównego v8. Przygotowałem małą wersję tego, jak to zrobić z izolatami/szafkami (https://gist.github.com/4341994). Zauważ, że to oznacza, że ​​musisz dostosować wszędzie, gdzie używasz V8, aby zablokować izolat zanim zrobisz cokolwiek innego! – je4d

+0

Dzięki. Zrobi to, ale wygląda na właściwe podejście. – marchaos

2

Problem polega na tym, że w addEventListener Persistent<Function> fn jest przydzielany na stosie, a następnie przenosisz wskaźnik do tego, aby użyć go jako kontekstu dla wywołania zwrotnego.

Ale ponieważ fn jest przydzielany na stosie, znika, gdy kończy się addEventListener. Tak więc zwracając się do callback'a context wskazujemy teraz na jakąś fałszywą wartość.

Powinieneś przydzielić trochę miejsca na sterty i umieścić tam wszystkie potrzebne dane w callback.

+1

wierzę wewnętrznie V8 przydziałów nic Persistent do sterty - to z pewnością zaprojektowany, aby być tam, dopóki jawnie dysponowania nim. – marchaos

+0

potwierdź. "Uporczywe uchwyty zapewniają odwołanie do obiektu JavaScript przydzielonego sterty" z https://developers.google.com/v8/embed –

13

Wiem, że to pytanie jest nieco stare, ale w nodejs v0.10 do wersji v0.12 nastąpiła całkiem spora aktualizacja. V8 zmienił zachowanie v8 :: Persistent. v8 :: Persistent nie dziedziczy po v8 :: Handle. Byłem aktualizację kodu i stwierdzili, że następujące pracował ...

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

Wierzę, że celem tej aktualizacji było utrudnić wystawiać wycieków pamięci. W węźle v0.10, byś zrobił coś jak następuje ...

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(); 
Powiązane problemy