Я пытаюсь вызвать зарегистрированную функцию JS, когда вызывается обратный вызов c ++, но я получаю segfault за то, что, как я полагаю, является проблемой области видимости.
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. Обратите внимание, что если я вызываю функцию обратного вызова напрямую со ссылкой на Persistent из addEventListener (), она выполняет функцию правильно.
Я предполагаю, что мне нужен шкафчик или изолят? Похоже, что libuv uv_queue_work () может решить эту проблему, но, поскольку я не запускаю поток, я не могу понять, как вы будете его использовать.
Когда вы объявляете 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&
, ср. 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.
Я знаю, что этот вопрос немного устарел, но в 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();
Проблема в том, что в addEventListener, Persistent<Function> fn
размещается в стеке, а затем вы берете указатель на него для использования в качестве контекста для обратного вызова.
Но потому что fn
выделяется в стеке, он исчезает, когда addEventListener
выходы. Так что с обратным вызовом context
Теперь укажите на какое-то поддельное значение.
Вы должны выделить некоторое пространство кучи и поместить все необходимые данные в callback
там.