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