[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/scream/node/master/src/node_isolate.cc [Back]  [Original]

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 


#define isolate_debugger_constructor NODE_VAR(isolate_debugger_constructor)

#define ISOLATEMESSAGE_SHARED_STREAM    0x0001


namespace node {

using v8::Arguments;
using v8::Array;
using v8::Context;
using v8::False;
using v8::Function;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Null;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::String;
using v8::True;
using v8::Undefined;
using v8::Value;
using v8::Undefined;

static volatile bool initialized;
static volatile int id;
static volatile int isolate_count;
static ngx_queue_t isolate_list;
static uv_mutex_t isolate_mutex;

#ifdef NDEBUG
# define IF_DEBUG(expr)
#else
# define IF_DEBUG(expr) expr;
#endif

template 
class Queue {
public:
  Queue() {
    if (uv_mutex_init(&mutex_)) abort();
    ngx_queue_init(&queue_);
  }

  ~Queue() {
    IF_DEBUG({
      uv_mutex_lock(&mutex_);
      assert(ngx_queue_empty(&queue_));
      uv_mutex_unlock(&mutex_);
    })
    uv_mutex_destroy(&mutex_);
  }

  void Produce(T item) {
    Message* m = new Message;
    m->item_ = item;

    uv_mutex_lock(&mutex_);
    ngx_queue_insert_tail(&queue_, &m->queue_);
    uv_mutex_unlock(&mutex_);
  }

  bool Consume(T& item) {
    ngx_queue_t* q = NULL;

    uv_mutex_lock(&mutex_);
    if (!ngx_queue_empty(&queue_)) {
      q = ngx_queue_head(&queue_);
      ngx_queue_remove(q);
    }
    uv_mutex_unlock(&mutex_);

    if (q == NULL) return false;

    Message* m = ngx_queue_data(q, Message, queue_);
    item = m->item_;
    delete m;

    return true;
  }

private:
  struct Message {
    ngx_queue_t queue_;
    T item_;
  };

  ngx_queue_t queue_;
  uv_mutex_t mutex_;
};


template 
class Channel {
public:
  typedef void (*Callback)(T item, void* arg);

  Channel(uv_loop_t* loop, Callback callback, void* arg) {
    callback_ = callback;
    arg_ = arg;
    uv_async_init(loop, &async_, OnMessage);
    uv_unref(loop);
  }

  ~Channel() {
    uv_ref(async_.loop);
    uv_close(reinterpret_cast(&async_), NULL);
  }

  void Send(T item) {
    queue_.Produce(item);
    uv_async_send(&async_);
  }

private:
  static void OnMessage(uv_async_t* handle, int status) {
    Channel* c = container_of(handle, Channel, async_);
    c->OnMessage();
  }

  void OnMessage() {
    T item;
    while (queue_.Consume(item)) callback_(item, arg_);
  }

  void* arg_;
  Callback callback_;
  uv_async_t async_;
  Queue queue_;
};


struct IsolateMessage {
  int flags;
  struct {
    size_t size_;
    char* buffer_;
  } data_;
  uv_stream_info_t shared_stream_info_;

  IsolateMessage(const char* buffer, size_t size,
                 uv_stream_info_t* shared_stream_info) {
    flags = 0;

    // make a copy for now
    data_.size_ = size;
    data_.buffer_ = new char[size];
    memcpy(data_.buffer_, buffer, size);

    if (shared_stream_info) {
      flags |= ISOLATEMESSAGE_SHARED_STREAM;
      shared_stream_info_ = *shared_stream_info;
    }
  }

  ~IsolateMessage() {
    delete[] data_.buffer_;
  }

  static void Free(char* data, void* arg) {
    IsolateMessage* msg = static_cast(arg);
    assert(data == msg->data_.buffer_);
    delete msg;
  }
};


class IsolateChannel: public Channel {
public:
  IsolateChannel(uv_loop_t* loop, Callback callback, void* arg)
    : Channel(loop, callback, arg)
  {
  }
};


Handle Isolate::Send(const Arguments& args) {
  HandleScope scope;

  Isolate* isolate = Isolate::GetCurrent();
  assert(Buffer::HasInstance(args[0]));
  assert(isolate->send_channel_ != NULL);

  Local obj = args[0]->ToObject();
  const char* data = Buffer::Data(obj);
  size_t size = Buffer::Length(obj);

  IsolateMessage* msg;

  if (args[1]->IsObject()) {
    uv_stream_info_t stream_info;

    Local send_stream_obj = args[1]->ToObject();
    assert(send_stream_obj->InternalFieldCount() > 0);
    StreamWrap* send_stream_wrap = static_cast(
        send_stream_obj->GetPointerFromInternalField(0));
    uv_stream_t* send_stream = send_stream_wrap->GetStream();
    int r = uv_export(send_stream, &stream_info);
    assert(r == 0);
    msg = new IsolateMessage(data, size, &stream_info);
  } else {
    msg = new IsolateMessage(data, size, NULL);
  }

  isolate->send_channel_->Send(msg);

  return Undefined();
}


Handle Isolate::Unref(const Arguments& args) {
  HandleScope scope;

  Isolate* isolate = Isolate::GetCurrent();
  uv_unref(isolate->loop_);

  return Undefined();
}


void Isolate::OnMessage(IsolateMessage* msg, void* arg) {
  HandleScope scope;

  Isolate* self = static_cast(arg);
  NODE_ISOLATE_CHECK(self);

  Buffer* buf = Buffer::New(msg->data_.buffer_, msg->data_.size_,
    IsolateMessage::Free, msg);

  int argc = 1;
  Handle argv[2] = {
    buf->handle_
  };

  if (msg->flags & ISOLATEMESSAGE_SHARED_STREAM) {
    // Instantiate the client javascript object and handle.
    Local pending_obj = TCPWrap::Instantiate();

    // Unwrap the client javascript object.
    assert(pending_obj->InternalFieldCount() > 0);
    TCPWrap* pending_wrap =
        static_cast(pending_obj->GetPointerFromInternalField(0));

    int r = uv_import(pending_wrap->GetStream(), &msg->shared_stream_info_);
    assert(r == 0);

    argv[1] = pending_obj;
    argc++;
  }

  MakeCallback(self->globals_.process, "_onmessage", argc, argv);
}


void Isolate::Initialize() {
  if (initialized) return;
  if (uv_mutex_init(&isolate_mutex)) abort();
  ngx_queue_init(&isolate_list);
  initialized = true;
}


int Isolate::Count() {
  int count;
  uv_mutex_lock(&isolate_mutex);
  count = isolate_count;
  uv_mutex_unlock(&isolate_mutex);
  return count;
}


Isolate::Isolate() {
  send_channel_ = NULL; // set (and deleted) by the parent isolate
  recv_channel_ = NULL;

  uv_mutex_lock(&isolate_mutex);
  assert(initialized && "node::Isolate::Initialize() hasn't been called");
  ngx_queue_insert_tail(&isolate_list, &isolate_list_);
  isolate_count++;
  id_ = ++id;
  uv_mutex_unlock(&isolate_mutex);

  if (id_ == 1) {
    loop_ = uv_default_loop();
  } else {
    loop_ = uv_loop_new();
    // Artificially ref the isolate loop so that the child
    // isolate stays alive by default.  process.exit will
    // unref the loop (see Isolate::Unref).
    uv_ref(loop_);
  }

  debug_state = kNone;
  debugger_instance = NULL;

  ngx_queue_init(&at_exit_callbacks_);

  v8_isolate_ = v8::Isolate::New();
  assert(v8_isolate_->GetData() == NULL);
  v8_isolate_->SetData(this);

  globals_init_ = false;
}


Isolate::~Isolate() {
  if (!argv_) return;
  for (size_t i = 0; argv_[i]; ++i) delete[] argv_[i];
  delete[] argv_;
}


struct globals* Isolate::Globals() {
  return &globals_;
}


void Isolate::AtExit(AtExitCallback callback, void* arg) {
  struct AtExitCallbackInfo* it = new AtExitCallbackInfo;

  //NODE_ISOLATE_CHECK(this);

  it->callback_ = callback;
  it->arg_ = arg;

  ngx_queue_insert_head(&at_exit_callbacks_, &it->queue_);
}


void Isolate::Enter() {
  v8_isolate_->Enter();

  if (v8_context_.IsEmpty()) {
    v8_context_ = Context::New();
  }
  v8_context_->Enter();

  if (!globals_init_) {
    globals_init_ = true;
    globals_init(&globals_);
  }

  NODE_ISOLATE_CHECK(this);
}


void Isolate::Exit() {
  NODE_ISOLATE_CHECK(this);
  v8_context_->Exit();
  v8_isolate_->Exit();
}


void Isolate::Dispose() {
  NODE_ISOLATE_CHECK(this);

  while (!ngx_queue_empty(&at_exit_callbacks_)) {
    ngx_queue_t* q = ngx_queue_head(&at_exit_callbacks_);
    ngx_queue_remove(q);

    AtExitCallbackInfo* it = ngx_queue_data(q, AtExitCallbackInfo, queue_);
    it->callback_(it->arg_);

    delete it;
  }

  assert(v8_context_->InContext());
  v8_context_->Exit();
  v8_context_.Clear();
  v8_context_.Dispose();

  v8_isolate_->Exit();
  v8_isolate_->Dispose();
  v8_isolate_ = NULL;

  uv_mutex_lock(&isolate_mutex);
  isolate_count--;
  ngx_queue_remove(&isolate_list_);
  assert(isolate_count >= 0);
  assert((isolate_count == 0 && ngx_queue_empty(&isolate_list))
      || (isolate_count > 0 && !ngx_queue_empty(&isolate_list)));
  uv_mutex_unlock(&isolate_mutex);
}


struct IsolateWrap: public ObjectWrap {
public:
  IsolateWrap(Isolate* parent_isolate) {
    parent_isolate_ = parent_isolate;

    uv_loop_t* parent_loop = parent_isolate->GetLoop();
    recv_channel_ = new IsolateChannel(
        parent_loop, IsolateWrap::OnMessage, this);

    isolate_ = new Isolate;
    send_channel_ = new IsolateChannel(
        isolate_->loop_, Isolate::OnMessage, isolate_);

    isolate_->send_channel_ = recv_channel_;
    isolate_->recv_channel_ = send_channel_;

    // TODO this could be folded into the regular channel
    uv_async_init(parent_loop, &child_exit_, AfterChildExit);
    isolate_->AtExit(AtChildExit, this);

    HandleScope scope;
    Local tpl = ObjectTemplate::New();
    tpl->SetInternalFieldCount(1);

    Local obj = tpl->NewInstance();
    Wrap(obj);
    Ref(); // unref'd when the child isolate exits

    obj->Set(String::NewSymbol("tid"),
             Integer::New(isolate_->id_));

    obj->Set(String::NewSymbol("send"),
             FunctionTemplate::New(Send)->GetFunction());
  }

  ~IsolateWrap() {
    delete isolate_;
    delete recv_channel_;
    delete send_channel_;
  }

  Isolate* GetIsolate() {
    return isolate_;
  }

private:
  // runs in the child thread
  static void AtChildExit(void* arg) {
    IsolateWrap* self = static_cast(arg);
    uv_async_send(&self->child_exit_);
  }

  // runs in the parent thread
  static void AfterChildExit(uv_async_t* handle, int status) {
    IsolateWrap* self = container_of(handle, IsolateWrap, child_exit_);
    self->OnExit();
  }

  void OnExit() {
    if (uv_thread_join(&isolate_->tid_)) abort();
    uv_close(reinterpret_cast(&child_exit_), NULL);
    MakeCallback(handle_, "onexit", 0, NULL);
    Unref(); // child is dead, it's safe to GC the JS object now
  }

  static void OnMessage(IsolateMessage* msg, void* arg) {
    IsolateWrap* self = static_cast(arg);
    self->OnMessage(msg);
  }

  void OnMessage(IsolateMessage* msg) {
    NODE_ISOLATE_CHECK(parent_isolate_);
    HandleScope scope;
    Buffer* buf = Buffer::New(
        msg->data_.buffer_, msg->data_.size_, IsolateMessage::Free, msg);

    int argc = 1;
    Handle argv[2] = {
      buf->handle_
    };

    if (msg->flags & ISOLATEMESSAGE_SHARED_STREAM) {
      // Instantiate the client javascript object and handle.
      Local pending_obj = TCPWrap::Instantiate();

      // Unwrap the client javascript object.
      assert(pending_obj->InternalFieldCount() > 0);
      TCPWrap* pending_wrap =
          static_cast(pending_obj->GetPointerFromInternalField(0));

      int r = uv_import(pending_wrap->GetStream(), &msg->shared_stream_info_);
      assert(r == 0);

      argv[1] = pending_obj;
      argc++;
    }

    MakeCallback(handle_, "onmessage", argc, argv);
  }

  // TODO merge with Isolate::Send(), it's almost identical
  static Handle Send(const Arguments& args) {
    HandleScope scope;
    IsolateWrap* self = Unwrap(args.This());
    assert(Buffer::HasInstance(args[0]));

    Local obj = args[0]->ToObject();
    const char* data = Buffer::Data(obj);
    size_t size = Buffer::Length(obj);

    IsolateMessage* msg;

    if (args[1]->IsObject()) {
      uv_stream_info_t stream_info;

      Local send_stream_obj = args[1]->ToObject();
      assert(send_stream_obj->InternalFieldCount() > 0);
      StreamWrap* send_stream_wrap = static_cast(
          send_stream_obj->GetPointerFromInternalField(0));
      uv_stream_t* send_stream = send_stream_wrap->GetStream();
      int r = uv_export(send_stream, &stream_info);
      assert(r == 0);
      msg = new IsolateMessage(data, size, &stream_info);
    } else {
      msg = new IsolateMessage(data, size, NULL);
    }

    self->send_channel_->Send(msg);
    return Undefined();
  }

  DISALLOW_IMPLICIT_CONSTRUCTORS(IsolateWrap);
  Isolate* isolate_;
  Isolate* parent_isolate_;
  IsolateChannel* send_channel_;
  IsolateChannel* recv_channel_;
  uv_async_t child_exit_; // side effect: keeps the parent's event loop alive
                          // until the child exits
};


static void RunIsolate(void* arg) {
  Isolate* isolate = static_cast(arg);
  isolate->Enter();
  StartThread(isolate, isolate->argc_, isolate->argv_);
  isolate->Dispose();
}


static Handle CreateIsolate(const Arguments& args) {
  HandleScope scope;

  assert(args[0]->IsArray());

  Local argv = args[0].As();
  assert(argv->Length() >= 2);

  Isolate* current_isolate = node::Isolate::GetCurrent();
  IsolateWrap* wrap = new IsolateWrap(current_isolate);
  Isolate* isolate = wrap->GetIsolate();

  // Copy over arguments into isolate
  isolate->argc_ = argv->Length();
  isolate->argv_ = new char*[isolate->argc_ + 1];
  for (int i = 0; i < isolate->argc_; ++i) {
    String::Utf8Value str(argv->Get(i));
    size_t size = 1 + strlen(*str);
    isolate->argv_[i] = new char[size];
    memcpy(isolate->argv_[i], *str, size);
  }
  isolate->argv_[isolate->argc_] = NULL;

  // If options object was provided
  if (args.Length() > 1) {
    Local options = args[1].As();
    Local opt_debug = options->Get(String::New("debug"));
    Local opt_debug_brk = options->Get(String::New("debugBrk"));

    // Handle .debug = true case
    if (opt_debug->IsFunction()) {
      isolate->debug_state = opt_debug_brk->IsTrue() ?
          Isolate::kDebugBrk
          :
          Isolate::kDebug;
      isolate->debugger_instance = IsolateDebugger::New(opt_debug);
    }
  }

  if (uv_thread_create(&isolate->tid_, RunIsolate, isolate))
    return Null(); // wrap is collected by the GC
  else
    return wrap->handle_;
}


static Handle CountIsolate(const Arguments& args) {
  HandleScope scope;
  return scope.Close(Integer::New(Isolate::Count()));
}


void InitIsolates(Handle target) {
  HandleScope scope;
  NODE_SET_METHOD(target, "create", CreateIsolate);
  NODE_SET_METHOD(target, "count", CountIsolate);

  IsolateDebugger::Initialize();
}


class IsolateDebuggerMessage {
 public:
  IsolateDebugger* d_;
  uint16_t* value_;
  int len_;

  IsolateDebuggerMessage(IsolateDebugger* d, uint16_t* value, int len) {
    d_ = d;
    value_ = new uint16_t[len];
    len_ = len;
    memcpy(value_, value, len * sizeof(value_[0]));
  }

  ~IsolateDebuggerMessage() {
    delete[] value_;
  }
};


void IsolateDebugger::Initialize() {
  HandleScope scope;

  Local t = FunctionTemplate::New(IsolateDebugger::New);
  isolate_debugger_constructor = Persistent::New(t);

  t->InstanceTemplate()->SetInternalFieldCount(1);
  t->SetClassName(String::NewSymbol("IsolateDebugger"));

  NODE_SET_PROTOTYPE_METHOD(t, "write", IsolateDebugger::Write);
}


IsolateDebugger::IsolateDebugger(Handle init) {
  debuggee_ = NULL;
  initialized_ = false;
  host_ = Isolate::GetCurrent();
  host_loop_ = host_->GetLoop();
  init_callback_fn_ = Persistent::New(init);

  // Init async handle to invoke js callback once
  // debugger will be initialized
  uv_async_init(host_loop_,
                &init_callback_,
                IsolateDebugger::InitCallback);
  init_callback_.data = reinterpret_cast(this);

  msg_channel_ = new Channel(
      host_loop_, MessageCallback, NULL);
}


IsolateDebugger::~IsolateDebugger() {
  init_callback_fn_.Clear();
  init_callback_fn_.Dispose();
  delete msg_channel_;
}


void IsolateDebugger::Init(void) {
  HandleScope scope;

  Isolate* isolate = Isolate::GetCurrent();

  debuggee_ = isolate;
  debuggee_v8_ = isolate->GetV8Isolate();
  v8::Debug::SetMessageHandler2(IsolateDebugger::DebugMessageHandler);

  // Expose v8debug for isolate

  if (isolate->debug_state == Isolate::kDebugBrk) {
    Local debugContext = v8::Debug::GetDebugContext();

    debugContext->SetSecurityToken(
        isolate->GetV8Context()->GetSecurityToken()
    );
    isolate->GetV8Context()->Global()->Set(
        String::New("v8debug"),
        debugContext->Global()
    );
  }

  initialized_ = true;

  uv_async_send(&init_callback_);
}


void IsolateDebugger::InitCallback(uv_async_t* c, int status) {
  assert(c->data != NULL);

  IsolateDebugger* d = reinterpret_cast(c->data);

  d->host_->Enter();
  HandleScope scope;

  Handle argv[1] = { d->handle_ };
  Function::Cast(*d->init_callback_fn_)->Call(d->handle_, 1, argv);

  d->host_->Exit();

  // Unreference loop
  uv_unref(d->host_loop_);
}


Handle IsolateDebugger::New(const Arguments& args) {
  HandleScope scope;

  IsolateDebugger* d = new IsolateDebugger(args[0]);
  d->Wrap(args.Holder());

  return args.This();
}


IsolateDebugger* IsolateDebugger::New(Handle init) {
  HandleScope scope;

  Handle argv[1] = { init };
  Handle i = isolate_debugger_constructor->GetFunction()->NewInstance(
      1,
      argv
  );

  return ObjectWrap::Unwrap(i);
}


Handle IsolateDebugger::Write(const Arguments& args) {
  HandleScope scope;

  if (args.Length() != 1) {
    return ThrowException(String::New(
        "IsolateDebugger::Write requires one argument"
    ));
  }

  IsolateDebugger* d = ObjectWrap::Unwrap(args.This());
  assert(d->initialized_);

  String::Value v(args[0]->ToString());
  v8::Debug::SendCommand(*v,
                         v.length(),
                         NULL,
                         d->debuggee_v8_);

  return Undefined();
}


void IsolateDebugger::DebugMessageHandler(const v8::Debug::Message& message) {
  IsolateDebugger* d = Isolate::GetCurrent()->debugger_instance;

  String::Value v(message.GetJSON());
  d->msg_channel_->Send(new IsolateDebuggerMessage(d, *v, v.length()));
}


void IsolateDebugger::MessageCallback(IsolateDebuggerMessage* msg, void*) {
  assert(msg != NULL);

  IsolateDebugger *d = msg->d_;
  // Enter parent isolate context
  d->host_->Enter();
  HandleScope scope;

  // debugger.onmessage should be a function!
  Handle argv[] = { String::New(msg->value_, msg->len_) };
  MakeCallback(d->handle_, "onmessage", ARRAY_SIZE(argv), argv);

  // Free memory allocated for message
  delete msg;

  // And leave isolate
  d->host_->Exit();
}


} // namespace node


NODE_MODULE(node_isolates, node::InitIsolates)

Web Proxy Viewer  |  New URL  |  Original Page