我正在尝试使用NDK在Android应用程序中嵌入v8。
我有一个看起来像这样的JNI模块(JNI映射代码未显示):
#include <jni.h>
#include <android/log.h>
#include <v8.h>
using namespace v8;
static jlong getMagicNumber() {
HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
Handle<String> source = String::New("40 + 2");
Handle<Script> script = Script::Compile(source);
Handle<Value> result = script->Run();
context.Dispose();
return result->NumberValue();
}
我第一次运行getMagicNumber
时,它正确运行并返回42.第二次尝试运行它时,它会崩溃。
具体而言,在v8的ASSERT
中看到的isolate.h
失败了:
// Returns the isolate inside which the current thread is running.
INLINE(static Isolate* Current()) {
Isolate* isolate = reinterpret_cast<Isolate*>(
Thread::GetExistingThreadLocal(isolate_key_));
ASSERT(isolate != NULL);
return isolate;
}
这听起来很像this problem,建议使用v8::Locker
获取“独立访问隔离区”。
通过在Locker l;
的顶部添加一个简单的getMagicNumber
,不再发生崩溃。当我没有注意时,自我修复的问题很容易破坏。
我对解决问题的原因只有最脆弱的理解,而且我正在以不推荐的方式收到我正在使用v8::Locker
的编译器警告。推荐的方法是为v8::Isolate
的构造函数提供一个v8::Locker
作为参数,但我不知道我应该如何“获得”一个隔离。
最终:根据v8的当前状态解决此问题的正确方法是什么,为什么?
答案 0 :(得分:6)
据我了解,V8隔离是V8运行时的一个实例,包含堆,垃圾收集器和零个或多个V8上下文。隔离区不是线程安全的,必须通过v8::Locker
保护。
通常,要使用V8,您必须先创建隔离:
v8::Isolate* isolate = v8::Isolate::New();
然后,使用任何线程的隔离:
v8::Locker locker(isolate);
v8::Isolate::Scope isolateScope(isolate);
此时线程拥有隔离,可以自由创建上下文,执行脚本等。
现在,为了非常简单的应用程序的优势,V8提供了默认隔离并放宽了锁定要求,但是如果您始终从同一个线程访问V8,则只能使用这些拐杖。我的猜测是你的应用程序失败了,因为第二次调用来自另一个线程。
答案 1 :(得分:0)
我现在正在学习V8,但我认为你需要打电话:
v8 :: Locker locker(isolate);
这将创建一个堆栈分配的Locker对象,该对象将阻止Isolate在另一个线程上使用。当前函数返回时,将自动调用此堆栈对象的析构函数,从而导致Isolate被解锁。
您需要致电:
v8 :: Isolate :: Scope isolateScope(isolate);
这将设置当前线程以运行此隔离。隔离区只能在一个线程上使用。 Locker强制执行此操作,但需要为当前线程配置Isolate本身。这将创建一个堆栈分配对象,该对象指定哪个Isolate与当前线程关联。就像Locker一样,当此变量超出范围时(当前函数返回时),将调用Scope析构函数以取消将Isolate设置为默认值。我认为这是必需的,因为许多V8 API调用需要引用Isolate,但不要将其作为参数。因此,他们需要一个可以直接访问的(可能通过每个线程变量)。
所有Isolate :: Scope类都在构造函数中调用isolate :: Enter(),并在析构函数中隔离:: Exit()。因此,如果您想要更多控制,可以自己调用Enter()/ Exit()。