凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

v8 で Context をスレッド間で共有してみた

はじめに

v8 を利用して、JavaScript 側で定義したコールバックが別スレッドから呼びたかったのでやってみました。具体的には、音声認識エンジン Julius にて、発話待機開始・発話開始・認識終了などのイベントが非同期でやってくるので、それらを JavaScript 側で補足して処理したいと思ったのが発端です。完全に非同期で実行する場合は v8::Isolate を使えば出来そうなコードはちらほらネット上にあったのですが、同一の Context をスレッド間で共有して実行する、という要件を満たすコードが見つからなかったのでまとめてみました。ただ、取り敢えず動きますが、コードを見ていただければ分かる通り、v8 もマルチスレッドプログラミングも良く分からずに書いたので、余りオススメ出来ない内容です(´・ω:;.:...

環境

  • Ubuntu 10.04 (古い…)
  • gcc 4.6.1 (古い…)

解説

コード
#include <iostream>
#include <thread>
#include <mutex>
#include <v8.h>

std::mutex m;

int main(int argc, char const* argv[])
{
  v8::HandleScope handle_scope;
  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
  global->Set(
    v8::String::New("print"),
    v8::FunctionTemplate::New([](const v8::Arguments& args)->v8::Handle<v8::Value> {
      v8::String::Utf8Value str(args[0]);
      std::cout << *str << std::endl;
      return v8::Undefined();
    })
  );
  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
  v8::Context::Scope scope(context);
  v8::Handle<v8::String> str = v8::String::New("print('Main thread');");
  v8::Handle<v8::Script> script = v8::Script::Compile(str);

  std::thread t([]{
    std::lock_guard<std::mutex> lk(m);
    {
      v8::Locker lock; // これをしないと欲しい Context がやってこないので print が undefined と怒られる
      v8::Context::Scope scope(v8::Context::GetCurrent());
      v8::HandleScope handle;
      v8::Handle<v8::String> str = v8::String::New("print('Sub thread');");
      v8::Handle<v8::Script> script = v8::Script::Compile(str);
      v8::Handle<v8::Value> result = script->Run();
    }
    v8::Context::GetCurrent()->Enter(); // Locker が破棄されると Context->Exit() してるっぽいので実行
  });

  {
    std::lock_guard<std::mutex> lk(m);
    v8::Handle<v8::Value> result = script->Run();
  }

  t.join();

  return 0;
}
コンパイル
g++ -std=c++0x hoge.cpp -L./v8/ -I./v8/include -lv8 -lpthread
実行
Main thread
Sub thread

なんか Exit されちゃったのでもう一回 Enter で入っておきましたよっと…、というかなーーり怪しいコードになっています。