mirror of https://github.com/lukechilds/node.git
Browse Source
It was decided that the performance benefits that isolates offer (faster spin-up times for worker processes, faster inter-worker communication, possibly a lower memory footprint) are not actual bottlenecks for most people and do not outweigh the potential stability issues and intrusive changes to the code base that first-class support for isolates requires. Hence, this commit backs out all isolates-related changes. Good bye, isolates. We hardly knew ye.v0.7.4-release
Ben Noordhuis
13 years ago
45 changed files with 413 additions and 2257 deletions
@ -1,109 +0,0 @@ |
|||||
|
|
||||
/*
|
|
||||
* Copyright (C) Igor Sysoev |
|
||||
*/ |
|
||||
|
|
||||
|
|
||||
#ifndef _NGX_QUEUE_H_INCLUDED_ |
|
||||
#define _NGX_QUEUE_H_INCLUDED_ |
|
||||
|
|
||||
#ifdef _WIN32 |
|
||||
# include <stddef.h> |
|
||||
#endif |
|
||||
|
|
||||
typedef struct ngx_queue_s ngx_queue_t; |
|
||||
|
|
||||
struct ngx_queue_s { |
|
||||
ngx_queue_t *prev; |
|
||||
ngx_queue_t *next; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_init(q) \ |
|
||||
(q)->prev = q; \ |
|
||||
(q)->next = q |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_empty(h) \ |
|
||||
(h == (h)->prev) |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_insert_head(h, x) \ |
|
||||
(x)->next = (h)->next; \ |
|
||||
(x)->next->prev = x; \ |
|
||||
(x)->prev = h; \ |
|
||||
(h)->next = x |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_insert_after ngx_queue_insert_head |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_insert_tail(h, x) \ |
|
||||
(x)->prev = (h)->prev; \ |
|
||||
(x)->prev->next = x; \ |
|
||||
(x)->next = h; \ |
|
||||
(h)->prev = x |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_head(h) \ |
|
||||
(h)->next |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_last(h) \ |
|
||||
(h)->prev |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_sentinel(h) \ |
|
||||
(h) |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_next(q) \ |
|
||||
(q)->next |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_prev(q) \ |
|
||||
(q)->prev |
|
||||
|
|
||||
|
|
||||
#if (NGX_DEBUG) |
|
||||
|
|
||||
#define ngx_queue_remove(x) \ |
|
||||
(x)->next->prev = (x)->prev; \ |
|
||||
(x)->prev->next = (x)->next; \ |
|
||||
(x)->prev = NULL; \ |
|
||||
(x)->next = NULL |
|
||||
|
|
||||
#else |
|
||||
|
|
||||
#define ngx_queue_remove(x) \ |
|
||||
(x)->next->prev = (x)->prev; \ |
|
||||
(x)->prev->next = (x)->next |
|
||||
|
|
||||
#endif |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_split(h, q, n) \ |
|
||||
(n)->prev = (h)->prev; \ |
|
||||
(n)->prev->next = n; \ |
|
||||
(n)->next = q; \ |
|
||||
(h)->prev = (q)->prev; \ |
|
||||
(h)->prev->next = h; \ |
|
||||
(q)->prev = n; |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_add(h, n) \ |
|
||||
(h)->prev->next = (n)->next; \ |
|
||||
(n)->next->prev = (h)->prev; \ |
|
||||
(h)->prev = (n)->prev; \ |
|
||||
(h)->prev->next = h; |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_data(q, type, link) \ |
|
||||
(type *) ((unsigned char *) q - offsetof(type, link)) |
|
||||
|
|
||||
|
|
||||
#define ngx_queue_foreach(q, h) \ |
|
||||
for ((q) = ngx_queue_head(h); (q) != (h); (q) = ngx_queue_next(q)) |
|
||||
|
|
||||
|
|
||||
#endif /* _NGX_QUEUE_H_INCLUDED_ */ |
|
@ -1,815 +0,0 @@ |
|||||
// 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 <v8.h> |
|
||||
#include <v8-debug.h> |
|
||||
#include <node.h> |
|
||||
#include <node_buffer.h> |
|
||||
#include <node_isolate.h> |
|
||||
#include <node_internals.h> |
|
||||
#include <node_object_wrap.h> |
|
||||
#include <tcp_wrap.h> |
|
||||
|
|
||||
#include <stdlib.h> |
|
||||
#include <string.h> |
|
||||
#include <assert.h> |
|
||||
|
|
||||
|
|
||||
#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 T> |
|
||||
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 T> |
|
||||
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<uv_handle_t*>(&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<T> 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<IsolateMessage*>(arg); |
|
||||
assert(data == msg->data_.buffer_); |
|
||||
delete msg; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
class IsolateChannel: public Channel<IsolateMessage*> { |
|
||||
public: |
|
||||
IsolateChannel(uv_loop_t* loop, Callback callback, void* arg) |
|
||||
: Channel<IsolateMessage*>(loop, callback, arg) |
|
||||
{ |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
Handle<Value> Isolate::Send(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
Isolate* isolate = Isolate::GetCurrent(); |
|
||||
assert(Buffer::HasInstance(args[0])); |
|
||||
assert(isolate->send_channel_ != NULL); |
|
||||
|
|
||||
Local<Object> 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<Object> send_stream_obj = args[1]->ToObject(); |
|
||||
assert(send_stream_obj->InternalFieldCount() > 0); |
|
||||
StreamWrap* send_stream_wrap = static_cast<StreamWrap*>( |
|
||||
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<Value> 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<Isolate*>(arg); |
|
||||
NODE_ISOLATE_CHECK(self); |
|
||||
|
|
||||
Buffer* buf = Buffer::New(msg->data_.buffer_, msg->data_.size_, |
|
||||
IsolateMessage::Free, msg); |
|
||||
|
|
||||
int argc = 1; |
|
||||
Handle<Value> argv[2] = { |
|
||||
buf->handle_ |
|
||||
}; |
|
||||
|
|
||||
if (msg->flags & ISOLATEMESSAGE_SHARED_STREAM) { |
|
||||
// Instantiate the client javascript object and handle.
|
|
||||
Local<Object> pending_obj = TCPWrap::Instantiate(); |
|
||||
|
|
||||
// Unwrap the client javascript object.
|
|
||||
assert(pending_obj->InternalFieldCount() > 0); |
|
||||
TCPWrap* pending_wrap = |
|
||||
static_cast<TCPWrap*>(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<ObjectTemplate> tpl = ObjectTemplate::New(); |
|
||||
tpl->SetInternalFieldCount(1); |
|
||||
|
|
||||
Local<Object> 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<IsolateWrap*>(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<uv_handle_t*>(&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<IsolateWrap*>(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<Value> argv[2] = { |
|
||||
buf->handle_ |
|
||||
}; |
|
||||
|
|
||||
if (msg->flags & ISOLATEMESSAGE_SHARED_STREAM) { |
|
||||
// Instantiate the client javascript object and handle.
|
|
||||
Local<Object> pending_obj = TCPWrap::Instantiate(); |
|
||||
|
|
||||
// Unwrap the client javascript object.
|
|
||||
assert(pending_obj->InternalFieldCount() > 0); |
|
||||
TCPWrap* pending_wrap = |
|
||||
static_cast<TCPWrap*>(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<Value> Send(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
IsolateWrap* self = Unwrap<IsolateWrap>(args.This()); |
|
||||
assert(Buffer::HasInstance(args[0])); |
|
||||
|
|
||||
Local<Object> 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<Object> send_stream_obj = args[1]->ToObject(); |
|
||||
assert(send_stream_obj->InternalFieldCount() > 0); |
|
||||
StreamWrap* send_stream_wrap = static_cast<StreamWrap*>( |
|
||||
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<Isolate*>(arg); |
|
||||
isolate->Enter(); |
|
||||
StartThread(isolate, isolate->argc_, isolate->argv_); |
|
||||
isolate->Dispose(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static Handle<Value> CreateIsolate(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
assert(args[0]->IsArray()); |
|
||||
|
|
||||
Local<Array> argv = args[0].As<Array>(); |
|
||||
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<Object> options = args[1].As<Object>(); |
|
||||
Local<Value> opt_debug = options->Get(String::New("debug")); |
|
||||
Local<Value> 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<Value> CountIsolate(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
return scope.Close(Integer::New(Isolate::Count())); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void InitIsolates(Handle<Object> 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<FunctionTemplate> t = FunctionTemplate::New(IsolateDebugger::New); |
|
||||
isolate_debugger_constructor = Persistent<FunctionTemplate>::New(t); |
|
||||
|
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1); |
|
||||
t->SetClassName(String::NewSymbol("IsolateDebugger")); |
|
||||
|
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "write", IsolateDebugger::Write); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
IsolateDebugger::IsolateDebugger(Handle<Value> init) { |
|
||||
debuggee_ = NULL; |
|
||||
initialized_ = false; |
|
||||
host_ = Isolate::GetCurrent(); |
|
||||
host_loop_ = host_->GetLoop(); |
|
||||
init_callback_fn_ = Persistent<Value>::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<void*>(this); |
|
||||
|
|
||||
msg_channel_ = new Channel<IsolateDebuggerMessage*>( |
|
||||
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<Context> 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<IsolateDebugger*>(c->data); |
|
||||
|
|
||||
d->host_->Enter(); |
|
||||
HandleScope scope; |
|
||||
|
|
||||
Handle<Value> 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<Value> IsolateDebugger::New(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
IsolateDebugger* d = new IsolateDebugger(args[0]); |
|
||||
d->Wrap(args.Holder()); |
|
||||
|
|
||||
return args.This(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
IsolateDebugger* IsolateDebugger::New(Handle<Value> init) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
Handle<Value> argv[1] = { init }; |
|
||||
Handle<Object> i = isolate_debugger_constructor->GetFunction()->NewInstance( |
|
||||
1, |
|
||||
argv |
|
||||
); |
|
||||
|
|
||||
return ObjectWrap::Unwrap<IsolateDebugger>(i); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
Handle<Value> IsolateDebugger::Write(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
if (args.Length() != 1) { |
|
||||
return ThrowException(String::New( |
|
||||
"IsolateDebugger::Write requires one argument" |
|
||||
)); |
|
||||
} |
|
||||
|
|
||||
IsolateDebugger* d = ObjectWrap::Unwrap<IsolateDebugger>(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<Value> 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) |
|
@ -1,185 +0,0 @@ |
|||||
// 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.
|
|
||||
|
|
||||
#ifndef SRC_NODE_ISOLATE_H_ |
|
||||
#define SRC_NODE_ISOLATE_H_ |
|
||||
|
|
||||
#include "v8.h" |
|
||||
#include "v8-debug.h" |
|
||||
#include "uv.h" |
|
||||
#include "node_vars.h" |
|
||||
#include "node_object_wrap.h" |
|
||||
#include "ngx-queue.h" |
|
||||
|
|
||||
#ifdef NDEBUG |
|
||||
# define NODE_ISOLATE_CHECK(ptr) ((void) (ptr)) |
|
||||
#else |
|
||||
# include <assert.h> |
|
||||
# define NODE_ISOLATE_CHECK(ptr) \ |
|
||||
do { \ |
|
||||
node::Isolate* data_ = node::Isolate::GetCurrent(); \ |
|
||||
assert(data_ == (ptr)); \ |
|
||||
} \ |
|
||||
while (0) |
|
||||
#endif |
|
||||
|
|
||||
|
|
||||
namespace node { |
|
||||
|
|
||||
template <class T> |
|
||||
|
|
||||
class Channel; |
|
||||
|
|
||||
class IsolateWrap; |
|
||||
class IsolateChannel; |
|
||||
class IsolateMessage; |
|
||||
class IsolateDebugger; |
|
||||
class IsolateDebuggerMessage; |
|
||||
|
|
||||
class Isolate { |
|
||||
public: |
|
||||
char** argv_; |
|
||||
int argc_; |
|
||||
uv_thread_t tid_; |
|
||||
|
|
||||
enum { |
|
||||
kNone, |
|
||||
kDebug, |
|
||||
kDebugBrk |
|
||||
} debug_state; |
|
||||
IsolateDebugger* debugger_instance; |
|
||||
|
|
||||
// Call this before instantiating any Isolate
|
|
||||
static void Initialize(); |
|
||||
static int Count(); |
|
||||
|
|
||||
typedef void (*AtExitCallback)(void* arg); |
|
||||
|
|
||||
static v8::Handle<v8::Value> Send(const v8::Arguments& args); |
|
||||
static v8::Handle<v8::Value> Unref(const v8::Arguments& args); |
|
||||
|
|
||||
static Isolate* GetCurrent() { |
|
||||
return reinterpret_cast<Isolate*>(v8::Isolate::GetCurrent()->GetData()); |
|
||||
} |
|
||||
|
|
||||
uv_loop_t* GetLoop() { |
|
||||
NODE_ISOLATE_CHECK(this); |
|
||||
return loop_; |
|
||||
} |
|
||||
|
|
||||
v8::Isolate* GetV8Isolate() { |
|
||||
NODE_ISOLATE_CHECK(this); |
|
||||
return v8_isolate_; |
|
||||
} |
|
||||
|
|
||||
v8::Handle<v8::Context> GetV8Context() { |
|
||||
NODE_ISOLATE_CHECK(this); |
|
||||
return v8_context_; |
|
||||
} |
|
||||
|
|
||||
/* Register a handler that should run when the current isolate exits.
|
|
||||
* Handlers run in LIFO order. |
|
||||
*/ |
|
||||
void AtExit(AtExitCallback callback, void *arg); |
|
||||
|
|
||||
struct globals* Globals(); |
|
||||
|
|
||||
unsigned int id_; |
|
||||
|
|
||||
// This constructor is used for every non-main thread
|
|
||||
Isolate(); |
|
||||
~Isolate(); |
|
||||
|
|
||||
void Enter(); |
|
||||
void Exit(); |
|
||||
|
|
||||
/* Shutdown the isolate. Call this method at thread death. */ |
|
||||
void Dispose(); |
|
||||
|
|
||||
private: |
|
||||
friend class IsolateWrap; |
|
||||
|
|
||||
struct AtExitCallbackInfo { |
|
||||
AtExitCallback callback_; |
|
||||
ngx_queue_t queue_; |
|
||||
void* arg_; |
|
||||
}; |
|
||||
|
|
||||
static void OnMessage(IsolateMessage*, void*); |
|
||||
|
|
||||
// Forbid implicit constructors and copy constructors
|
|
||||
void operator=(const Isolate&) {} |
|
||||
Isolate(const Isolate&) {} |
|
||||
|
|
||||
ngx_queue_t isolate_list_; // linked list of all isolates
|
|
||||
ngx_queue_t at_exit_callbacks_; |
|
||||
v8::Persistent<v8::Context> v8_context_; |
|
||||
v8::Isolate* v8_isolate_; |
|
||||
IsolateChannel* send_channel_; |
|
||||
IsolateChannel* recv_channel_; |
|
||||
uv_loop_t* loop_; |
|
||||
|
|
||||
// Global variables for this isolate.
|
|
||||
struct globals globals_; |
|
||||
bool globals_init_; |
|
||||
}; |
|
||||
|
|
||||
class IsolateDebugger : ObjectWrap { |
|
||||
public: |
|
||||
static void Initialize(); |
|
||||
void Init(); |
|
||||
static void InitCallback(uv_async_t* c, int status); |
|
||||
|
|
||||
static v8::Handle<v8::Value> New(const v8::Arguments& args); |
|
||||
static IsolateDebugger* New(v8::Handle<v8::Value> init); |
|
||||
|
|
||||
static v8::Handle<v8::Value> Write(const v8::Arguments& args); |
|
||||
|
|
||||
static void DebugMessageHandler(const v8::Debug::Message& message); |
|
||||
static void MessageCallback(IsolateDebuggerMessage* msg, void*); |
|
||||
|
|
||||
IsolateDebugger(v8::Handle<v8::Value> init); |
|
||||
~IsolateDebugger(); |
|
||||
|
|
||||
protected: |
|
||||
Isolate* host_; |
|
||||
uv_loop_t* host_loop_; |
|
||||
|
|
||||
uv_async_t init_callback_; |
|
||||
v8::Persistent<v8::Value> init_callback_fn_; |
|
||||
|
|
||||
bool initialized_; |
|
||||
Isolate* debuggee_; |
|
||||
v8::Isolate* debuggee_v8_; |
|
||||
|
|
||||
struct debug_msg_s { |
|
||||
uint16_t* value; |
|
||||
int len; |
|
||||
|
|
||||
IsolateDebugger* d; |
|
||||
}; |
|
||||
|
|
||||
Channel<IsolateDebuggerMessage*>* msg_channel_; |
|
||||
}; |
|
||||
|
|
||||
} // namespace node
|
|
||||
|
|
||||
#endif // SRC_NODE_ISOLATE_H_
|
|
@ -1,48 +0,0 @@ |
|||||
#include <node_vars.h> |
|
||||
#include <node_isolate.h> |
|
||||
#if HAVE_OPENSSL |
|
||||
# include <node_crypto.h> |
|
||||
#endif |
|
||||
#include <string.h> |
|
||||
|
|
||||
namespace node { |
|
||||
|
|
||||
// For now we just statically initialize the globals structure. Later there
|
|
||||
// will be one struct globals for each isolate.
|
|
||||
|
|
||||
void globals_init(struct globals* g) { |
|
||||
memset(g, 0, sizeof(struct globals)); |
|
||||
|
|
||||
#ifdef OPENSSL_NPN_NEGOTIATED |
|
||||
g->use_npn = true; |
|
||||
#else |
|
||||
g->use_npn = false; |
|
||||
#endif |
|
||||
|
|
||||
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB |
|
||||
g->use_sni = true; |
|
||||
#else |
|
||||
g->use_sni = false; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#if HAVE_ISOLATES |
|
||||
struct globals* globals_get() { |
|
||||
node::Isolate* isolate = node::Isolate::GetCurrent(); |
|
||||
return isolate->Globals(); |
|
||||
} |
|
||||
#else |
|
||||
static struct globals g_struct; |
|
||||
static struct globals* g_ptr; |
|
||||
|
|
||||
struct globals* globals_get() { |
|
||||
if (!g_ptr) { |
|
||||
g_ptr = &g_struct; |
|
||||
globals_init(g_ptr); |
|
||||
} |
|
||||
return g_ptr; |
|
||||
} |
|
||||
#endif // HAVE_ISOLATES
|
|
||||
|
|
||||
} // namespace node
|
|
@ -1,197 +0,0 @@ |
|||||
#ifndef NODE_VARS_H |
|
||||
#define NODE_VARS_H |
|
||||
|
|
||||
// This file contains all Isolate-local variables. We allow people to
|
|
||||
// compile Node either with Isolates or without. In the case that they
|
|
||||
// compile without isolates, these will be static variables.
|
|
||||
|
|
||||
#include <v8.h> |
|
||||
#include <uv.h> |
|
||||
#include <http_parser.h> |
|
||||
|
|
||||
#if defined(_MSC_VER) |
|
||||
# define PATH_MAX MAX_PATH |
|
||||
#endif |
|
||||
|
|
||||
#ifndef PATH_MAX |
|
||||
# define PATH_MAX 4096 |
|
||||
#endif |
|
||||
|
|
||||
namespace node { |
|
||||
|
|
||||
|
|
||||
#define NODE_VAR(x) (globals_get()->x) |
|
||||
|
|
||||
struct globals { |
|
||||
// node.cc
|
|
||||
v8::Persistent<v8::Object> process; |
|
||||
v8::Persistent<v8::String> errno_symbol; |
|
||||
v8::Persistent<v8::String> syscall_symbol; |
|
||||
v8::Persistent<v8::String> errpath_symbol; |
|
||||
v8::Persistent<v8::String> code_symbol; |
|
||||
v8::Persistent<v8::String> rss_symbol; |
|
||||
v8::Persistent<v8::String> heap_total_symbol; |
|
||||
v8::Persistent<v8::String> heap_used_symbol; |
|
||||
v8::Persistent<v8::String> listeners_symbol; |
|
||||
v8::Persistent<v8::String> uncaught_exception_symbol; |
|
||||
v8::Persistent<v8::String> emit_symbol; |
|
||||
uv_check_t check_tick_watcher; |
|
||||
uv_prepare_t prepare_tick_watcher; |
|
||||
uv_idle_t tick_spinner; |
|
||||
bool need_tick_cb; |
|
||||
v8::Persistent<v8::String> tick_callback_sym; |
|
||||
bool use_npn; |
|
||||
bool use_sni; |
|
||||
// Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this
|
|
||||
// scoped at file-level rather than method-level to avoid excess stack usage.
|
|
||||
char getbuf[PATH_MAX + 1]; |
|
||||
// We need to notify V8 when we're idle so that it can run the garbage
|
|
||||
// collector. The interface to this is V8::IdleNotification(). It returns
|
|
||||
// true if the heap hasn't be fully compacted, and needs to be run again.
|
|
||||
// Returning false means that it doesn't have anymore work to do.
|
|
||||
//
|
|
||||
// A rather convoluted algorithm has been devised to determine when Node is
|
|
||||
// idle. You'll have to figure it out for yourself.
|
|
||||
uv_check_t gc_check; |
|
||||
uv_idle_t gc_idle; |
|
||||
uv_timer_t gc_timer; |
|
||||
bool need_gc; |
|
||||
# define FAST_TICK 700. |
|
||||
# define GC_WAIT_TIME 5000. |
|
||||
# define RPM_SAMPLES 100 |
|
||||
int64_t tick_times[RPM_SAMPLES]; |
|
||||
int tick_time_head; |
|
||||
int uncaught_exception_counter; |
|
||||
v8::Persistent<v8::Object> binding_cache; |
|
||||
v8::Persistent<v8::Array> module_load_list; |
|
||||
v8::Isolate* node_isolate; |
|
||||
volatile bool debugger_running; |
|
||||
double prog_start_time; |
|
||||
|
|
||||
// stream_wrap.cc
|
|
||||
size_t slab_used; |
|
||||
uv_stream_t* handle_that_last_alloced; |
|
||||
v8::Persistent<v8::String> slab_sym; |
|
||||
v8::Persistent<v8::String> buffer_sym; |
|
||||
v8::Persistent<v8::String> write_queue_size_sym; |
|
||||
bool stream_wrap_initialized; |
|
||||
|
|
||||
// tcp_wrap.cc
|
|
||||
v8::Persistent<v8::Function> tcpConstructor; |
|
||||
v8::Persistent<v8::String> family_symbol; |
|
||||
v8::Persistent<v8::String> address_symbol; |
|
||||
v8::Persistent<v8::String> port_symbol; |
|
||||
|
|
||||
// node_http_parser.cc
|
|
||||
v8::Persistent<v8::String> on_headers_sym; |
|
||||
v8::Persistent<v8::String> on_headers_complete_sym; |
|
||||
v8::Persistent<v8::String> on_body_sym; |
|
||||
v8::Persistent<v8::String> on_message_complete_sym; |
|
||||
v8::Persistent<v8::String> delete_sym; |
|
||||
v8::Persistent<v8::String> get_sym; |
|
||||
v8::Persistent<v8::String> head_sym; |
|
||||
v8::Persistent<v8::String> post_sym; |
|
||||
v8::Persistent<v8::String> put_sym; |
|
||||
v8::Persistent<v8::String> connect_sym; |
|
||||
v8::Persistent<v8::String> options_sym; |
|
||||
v8::Persistent<v8::String> trace_sym; |
|
||||
v8::Persistent<v8::String> patch_sym; |
|
||||
v8::Persistent<v8::String> copy_sym; |
|
||||
v8::Persistent<v8::String> lock_sym; |
|
||||
v8::Persistent<v8::String> mkcol_sym; |
|
||||
v8::Persistent<v8::String> move_sym; |
|
||||
v8::Persistent<v8::String> propfind_sym; |
|
||||
v8::Persistent<v8::String> proppatch_sym; |
|
||||
v8::Persistent<v8::String> unlock_sym; |
|
||||
v8::Persistent<v8::String> report_sym; |
|
||||
v8::Persistent<v8::String> mkactivity_sym; |
|
||||
v8::Persistent<v8::String> checkout_sym; |
|
||||
v8::Persistent<v8::String> merge_sym; |
|
||||
v8::Persistent<v8::String> msearch_sym; |
|
||||
v8::Persistent<v8::String> notify_sym; |
|
||||
v8::Persistent<v8::String> subscribe_sym; |
|
||||
v8::Persistent<v8::String> unsubscribe_sym; |
|
||||
v8::Persistent<v8::String> unknown_method_sym; |
|
||||
v8::Persistent<v8::String> method_sym; |
|
||||
v8::Persistent<v8::String> status_code_sym; |
|
||||
v8::Persistent<v8::String> http_version_sym; |
|
||||
v8::Persistent<v8::String> version_major_sym; |
|
||||
v8::Persistent<v8::String> version_minor_sym; |
|
||||
v8::Persistent<v8::String> should_keep_alive_sym; |
|
||||
v8::Persistent<v8::String> upgrade_sym; |
|
||||
v8::Persistent<v8::String> headers_sym; |
|
||||
v8::Persistent<v8::String> url_sym; |
|
||||
struct http_parser_settings settings; |
|
||||
// This is a hack to get the current_buffer to the callbacks with the least
|
|
||||
// amount of overhead. Nothing else will run while http_parser_execute()
|
|
||||
// runs, therefore this pointer can be set and used for the execution.
|
|
||||
v8::Local<v8::Value>* current_buffer; |
|
||||
char* current_buffer_data; |
|
||||
size_t current_buffer_len; |
|
||||
|
|
||||
// node_file.cc
|
|
||||
v8::Persistent<v8::String> encoding_symbol; |
|
||||
v8::Persistent<v8::String> buf_symbol; |
|
||||
v8::Persistent<v8::String> oncomplete_sym; |
|
||||
v8::Persistent<v8::FunctionTemplate> stats_constructor_template; |
|
||||
v8::Persistent<v8::String> dev_symbol; |
|
||||
v8::Persistent<v8::String> ino_symbol; |
|
||||
v8::Persistent<v8::String> mode_symbol; |
|
||||
v8::Persistent<v8::String> nlink_symbol; |
|
||||
v8::Persistent<v8::String> uid_symbol; |
|
||||
v8::Persistent<v8::String> gid_symbol; |
|
||||
v8::Persistent<v8::String> rdev_symbol; |
|
||||
v8::Persistent<v8::String> size_symbol; |
|
||||
v8::Persistent<v8::String> blksize_symbol; |
|
||||
v8::Persistent<v8::String> blocks_symbol; |
|
||||
v8::Persistent<v8::String> atime_symbol; |
|
||||
v8::Persistent<v8::String> mtime_symbol; |
|
||||
v8::Persistent<v8::String> ctime_symbol; |
|
||||
|
|
||||
// node_zlib.cc
|
|
||||
v8::Persistent<v8::String> callback_sym; |
|
||||
|
|
||||
// node_crypto.cc
|
|
||||
v8::Persistent<v8::String> subject_symbol; |
|
||||
v8::Persistent<v8::String> subjectaltname_symbol; |
|
||||
v8::Persistent<v8::String> modulus_symbol; |
|
||||
v8::Persistent<v8::String> exponent_symbol; |
|
||||
v8::Persistent<v8::String> issuer_symbol; |
|
||||
v8::Persistent<v8::String> valid_from_symbol; |
|
||||
v8::Persistent<v8::String> valid_to_symbol; |
|
||||
v8::Persistent<v8::String> fingerprint_symbol; |
|
||||
v8::Persistent<v8::String> name_symbol; |
|
||||
v8::Persistent<v8::String> version_symbol; |
|
||||
v8::Persistent<v8::String> ext_key_usage_symbol; |
|
||||
v8::Persistent<v8::FunctionTemplate> secure_context_constructor; |
|
||||
|
|
||||
// node_buffer.cc
|
|
||||
v8::Persistent<v8::String> length_symbol; |
|
||||
v8::Persistent<v8::String> chars_written_sym; |
|
||||
v8::Persistent<v8::String> write_sym; |
|
||||
v8::Persistent<v8::FunctionTemplate> buffer_constructor_template; |
|
||||
|
|
||||
// node_script.cc
|
|
||||
v8::Persistent<v8::FunctionTemplate> wrapped_context_constructor; |
|
||||
v8::Persistent<v8::FunctionTemplate> wrapped_script_constructor; |
|
||||
|
|
||||
// node_isolate.cc
|
|
||||
v8::Persistent<v8::FunctionTemplate> isolate_debugger_constructor; |
|
||||
|
|
||||
// node_signal_watcher.cc
|
|
||||
v8::Persistent<v8::String> callback_symbol; |
|
||||
v8::Persistent<v8::FunctionTemplate> signal_watcher_constructor_template; |
|
||||
|
|
||||
// cares_wrap.cc
|
|
||||
::ares_channel ares_channel; |
|
||||
}; |
|
||||
|
|
||||
// Initialize globals struct.
|
|
||||
void globals_init(struct globals*); |
|
||||
|
|
||||
// Get the globals struct for the current Isolate. The returned pointer is
|
|
||||
// already initialized.
|
|
||||
struct globals* globals_get(); |
|
||||
|
|
||||
} // namespace node
|
|
||||
#endif // NODE_VARS_H
|
|
@ -1,55 +0,0 @@ |
|||||
#include <node.h> |
|
||||
#include <v8.h> |
|
||||
#include <uv.h> |
|
||||
|
|
||||
using namespace v8; |
|
||||
|
|
||||
extern "C" { |
|
||||
void init(Handle<Object> target); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#define BUFSIZE 1024 |
|
||||
static uint8_t buf[BUFSIZE]; |
|
||||
static uv_mutex_t lock; |
|
||||
|
|
||||
|
|
||||
Handle<Value> Get(const Arguments& args) { |
|
||||
HandleScope scope; |
|
||||
|
|
||||
int index = args[0]->Uint32Value(); |
|
||||
|
|
||||
if (index < 0 || BUFSIZE <= index) { |
|
||||
return ThrowException(Exception::Error(String::New("out of bounds"))); |
|
||||
} |
|
||||
|
|
||||
return scope.Close(Integer::New(buf[index])); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
Handle<Value> Set(const Arguments& args) { |
|
||||
uv_mutex_lock(&lock); |
|
||||
HandleScope scope; |
|
||||
|
|
||||
int index = args[0]->Uint32Value(); |
|
||||
|
|
||||
if (index < 0 || BUFSIZE <= index) { |
|
||||
return ThrowException(Exception::Error(String::New("out of bounds"))); |
|
||||
} |
|
||||
|
|
||||
buf[index] = args[1]->Uint32Value(); |
|
||||
|
|
||||
Local<Integer> val = Integer::New(buf[index]); |
|
||||
|
|
||||
uv_mutex_unlock(&lock); |
|
||||
|
|
||||
return scope.Close(val); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void init(Handle<Object> target) { |
|
||||
NODE_SET_METHOD(target, "get", Get); |
|
||||
NODE_SET_METHOD(target, "set", Set); |
|
||||
target->Set(String::New("length"), Integer::New(BUFSIZE)); |
|
||||
uv_mutex_init(&lock); |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
{ |
|
||||
'targets': [ |
|
||||
{ |
|
||||
'target_name': 'binding', |
|
||||
'sources': [ 'binding.cc' ] |
|
||||
} |
|
||||
] |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
var assert = require('assert'); |
|
||||
var binding = require('./out/Release/binding'); |
|
||||
var isolates = process.binding('isolates'); |
|
||||
|
|
||||
console.log("binding.length =", binding.length); |
|
||||
|
|
||||
if (process.tid === 1) { |
|
||||
var isolate = isolates.create(process.argv); |
|
||||
for (var i = 0; i < binding.length; i++) { |
|
||||
console.log('parent', |
|
||||
'binding.set(' + i + ', ' + i + ')', |
|
||||
binding.set(i, i)); |
|
||||
} |
|
||||
} else { |
|
||||
for (var i = 0; i < binding.length; i++) { |
|
||||
console.log('child', 'binding.get(' + i + ')', binding.get(i)); |
|
||||
} |
|
||||
} |
|
||||
|
|
@ -1,68 +0,0 @@ |
|||||
var isolates = process.binding('isolates'); |
|
||||
var assert = require('assert'); |
|
||||
|
|
||||
var N_ISOLATES = 4; |
|
||||
var N_MESSAGES = 20; |
|
||||
var N_MESSAGES_PER_TICK = 4; |
|
||||
|
|
||||
assert(N_MESSAGES % N_MESSAGES_PER_TICK == 0); |
|
||||
|
|
||||
if (process.tid === 1) |
|
||||
master(); |
|
||||
else |
|
||||
child(); |
|
||||
|
|
||||
function master() { |
|
||||
for (var i = 0; i < N_ISOLATES; ++i) spawn(); |
|
||||
|
|
||||
function spawn() { |
|
||||
var isolate = isolates.create(process.argv); |
|
||||
|
|
||||
var gotExit = false; // exit event emitted?
|
|
||||
var msgId = 0; // message IDs seen so far
|
|
||||
var tick = 0; |
|
||||
|
|
||||
isolate.onexit = function() { |
|
||||
gotExit = true; |
|
||||
}; |
|
||||
|
|
||||
isolate.onmessage = function(buf) { |
|
||||
var msg = JSON.parse(buf); |
|
||||
assert.equal(msg.id, msgId + 1); // verify that messages arrive in order
|
|
||||
assert.equal(msg.tick, tick); // and on the proper tick (=full mq drain)
|
|
||||
msgId = msg.id; |
|
||||
if (msgId % N_MESSAGES_PER_TICK == 0) tick++; |
|
||||
isolate.send(buf); |
|
||||
}; |
|
||||
|
|
||||
process.on('exit', function() { |
|
||||
assert.equal(gotExit, true); |
|
||||
assert.equal(msgId, N_MESSAGES); |
|
||||
assert.equal(tick, N_MESSAGES / N_MESSAGES_PER_TICK); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function child() { |
|
||||
var msgId = 0; |
|
||||
var tick = 0; |
|
||||
|
|
||||
function send() { |
|
||||
// Send multiple messages, verify that the message queue
|
|
||||
// is completely drained on each tick of the event loop.
|
|
||||
for (var i = 0; i < N_MESSAGES_PER_TICK; ++i) { |
|
||||
process.send({tick:tick, id:++msgId}); |
|
||||
} |
|
||||
|
|
||||
if (msgId < N_MESSAGES) { |
|
||||
setTimeout(send, 10); |
|
||||
} |
|
||||
|
|
||||
tick++; |
|
||||
} |
|
||||
|
|
||||
send(); |
|
||||
|
|
||||
process._onmessage = function(m) { |
|
||||
}; |
|
||||
} |
|
@ -1,60 +0,0 @@ |
|||||
var fs = require('fs'); |
|
||||
var http = require('http'); |
|
||||
var isolates = process.binding('isolates'); |
|
||||
|
|
||||
console.log("count: %d", isolates.count()); |
|
||||
|
|
||||
if (process.tid === 1) { |
|
||||
var isolate = isolates.create(process.argv, { |
|
||||
debug: function init(d) { |
|
||||
d.onmessage = function(data) { |
|
||||
data = JSON.parse(data); |
|
||||
if (data.event === 'break') { |
|
||||
d.write(JSON.stringify({ |
|
||||
type: 'request', |
|
||||
seq: 1, |
|
||||
command: 'continue' |
|
||||
})); |
|
||||
} |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
isolate.onmessage = function() { |
|
||||
console.error("onmessage"); |
|
||||
}; |
|
||||
isolate.onexit = function() { |
|
||||
console.error("onexit"); |
|
||||
}; |
|
||||
|
|
||||
console.error("master"); |
|
||||
fs.stat(__dirname, function(err, stat) { |
|
||||
if (err) throw err; |
|
||||
console.error('thread 1', stat.mtime); |
|
||||
}); |
|
||||
|
|
||||
setTimeout(function() { |
|
||||
fs.stat(__dirname, function(err, stat) { |
|
||||
if (err) throw err; |
|
||||
console.error('thread 1', stat.mtime); |
|
||||
}); |
|
||||
}, 500); |
|
||||
|
|
||||
console.log("thread 1 count: %d", isolates.count()); |
|
||||
} else { |
|
||||
console.error("slave"); |
|
||||
fs.stat(__dirname, function(err, stat) { |
|
||||
if (err) throw err; |
|
||||
console.error('thread 2', stat.mtime); |
|
||||
}); |
|
||||
|
|
||||
setTimeout(function() { |
|
||||
fs.stat(__dirname, function(err, stat) { |
|
||||
if (err) throw err; |
|
||||
console.error('thread 2', stat.mtime); |
|
||||
process.exit(); |
|
||||
}); |
|
||||
}, 500); |
|
||||
|
|
||||
console.error("thread 2 count: %d", isolates.count()); |
|
||||
} |
|
@ -1,13 +0,0 @@ |
|||||
// Skip this test if Node is not compiled with isolates support.
|
|
||||
if (!process.features.isolates) return; |
|
||||
|
|
||||
var assert = require('assert'); |
|
||||
|
|
||||
// This is the same test as test-child-process-fork except it uses isolates
|
|
||||
// instead of processes. process.TEST_ISOLATE is a ghetto method of passing
|
|
||||
// some information into the other test.
|
|
||||
process.TEST_ISOLATE = true; |
|
||||
require('./test-child-process-fork'); |
|
||||
|
|
||||
var numThreads = process.binding('isolates').count(); |
|
||||
assert.ok(numThreads > 1); |
|
@ -1,13 +0,0 @@ |
|||||
// Skip this test if Node is not compiled with isolates support.
|
|
||||
if (!process.features.isolates) return; |
|
||||
|
|
||||
var assert = require('assert'); |
|
||||
|
|
||||
// This is the same test as test-child-process-fork2 except it uses isolates
|
|
||||
// instead of processes. process.TEST_ISOLATE is a ghetto method of passing
|
|
||||
// some information into the other test.
|
|
||||
process.TEST_ISOLATE = true; |
|
||||
require('./test-child-process-fork2'); |
|
||||
|
|
||||
var numThreads = process.binding('isolates').count(); |
|
||||
assert.ok(numThreads > 1); |
|
@ -1,13 +0,0 @@ |
|||||
// Skip this test if Node is not compiled with isolates support.
|
|
||||
if (!process.features.isolates) return; |
|
||||
|
|
||||
var assert = require('assert'); |
|
||||
|
|
||||
// This is the same test as test-child-process-fork3 except it uses isolates
|
|
||||
// instead of processes. process.TEST_ISOLATE is a ghetto method of passing
|
|
||||
// some information into the other test.
|
|
||||
process.TEST_ISOLATE = true; |
|
||||
require('./test-child-process-fork3'); |
|
||||
|
|
||||
var numThreads = process.binding('isolates').count(); |
|
||||
assert.ok(numThreads > 1); |
|
Loading…
Reference in new issue