@ -19,10 +19,12 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
# include <v8.h>
# include <node.h>
# include <node_buffer.h>
# include <node_isolate.h>
# include <node_internals.h>
# include <v8 .h>
# include <node_object_wrap .h>
# include <stdlib.h>
# include <string.h>
@ -34,6 +36,7 @@ namespace node {
using v8 : : Arguments ;
using v8 : : Array ;
using v8 : : False ;
using v8 : : FunctionTemplate ;
using v8 : : Handle ;
using v8 : : HandleScope ;
using v8 : : Integer ;
@ -41,12 +44,11 @@ using v8::Local;
using v8 : : Null ;
using v8 : : Object ;
using v8 : : ObjectTemplate ;
using v8 : : Persistent ;
using v8 : : String ;
using v8 : : True ;
using v8 : : Value ;
static char magic_isolate_cookie_ [ ] = " magic isolate cookie " ;
using v8 : : Undefined ;
static volatile bool initialized ;
static volatile int id ;
@ -55,6 +57,165 @@ static uv_mutex_t list_lock;
static ngx_queue_t list_head ;
# 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_ ) ;
}
T Consume ( ) {
uv_mutex_lock ( & mutex_ ) ;
ngx_queue_t * q = ngx_queue_head ( & queue_ ) ;
ngx_queue_remove ( q ) ;
uv_mutex_unlock ( & mutex_ ) ;
Message * m = ngx_queue_data ( q , Message , queue_ ) ;
T item = m - > item_ ;
delete m ;
return item ;
}
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 = queue_ . Consume ( ) ;
callback_ ( item , arg_ ) ;
}
void * arg_ ;
Callback callback_ ;
uv_async_t async_ ;
Queue < T > queue_ ;
} ;
struct IsolateMessage {
size_t size_ ;
char * data_ ;
IsolateMessage ( const char * data , size_t size ) {
// make a copy for now
size_ = size ;
data_ = new char [ size ] ;
memcpy ( data_ , data , size ) ;
}
~ IsolateMessage ( ) {
delete [ ] data_ ;
}
static void Free ( char * data , void * arg ) {
IsolateMessage * msg = static_cast < IsolateMessage * > ( arg ) ;
assert ( data = = msg - > data_ ) ;
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 = new IsolateMessage ( data , size ) ;
isolate - > send_channel_ - > Send ( msg ) ;
return Undefined ( ) ;
}
void Isolate : : OnMessage ( IsolateMessage * msg , void * arg ) {
HandleScope scope ;
Isolate * self = static_cast < Isolate * > ( arg ) ;
assert ( uv_thread_self ( ) = = self - > tid_ ) ;
NODE_ISOLATE_CHECK ( self ) ;
Buffer * buf = Buffer : : New ( msg - > data_ , msg - > size_ , IsolateMessage : : Free , msg ) ;
Handle < Value > argv [ ] = { buf - > handle_ } ;
MakeCallback ( self - > globals_ . process , " _onmessage " , ARRAY_SIZE ( argv ) , argv ) ;
}
void Isolate : : Initialize ( ) {
if ( ! initialized ) {
initialized = true ;
@ -93,6 +254,9 @@ void Isolate::JoinAll() {
Isolate : : Isolate ( ) {
send_channel_ = NULL ; // set (and deleted) by the parent isolate
recv_channel_ = NULL ;
uv_mutex_lock ( & list_lock ) ;
assert ( initialized & & " node::Isolate::Initialize() hasn't been called " ) ;
@ -123,6 +287,13 @@ Isolate::Isolate() {
}
Isolate : : ~ Isolate ( ) {
if ( ! argv_ ) return ;
for ( size_t i = 0 ; argv_ [ i ] ; + + i ) delete [ ] argv_ [ i ] ;
delete [ ] argv_ ;
}
struct globals * Isolate : : Globals ( ) {
return & globals_ ;
}
@ -131,12 +302,12 @@ struct globals* Isolate::Globals() {
void Isolate : : AtExit ( AtExitCallback callback , void * arg ) {
struct AtExitCallbackInfo * it = new AtExitCallbackInfo ;
NODE_ISOLATE_CHECK ( this ) ;
//NODE_ISOLATE_CHECK(this);
it - > callback_ = callback ;
it - > arg_ = arg ;
ngx_queue_insert_head ( & at_exit_callbacks_ , & it - > at_exit_callbacks _) ;
ngx_queue_insert_head ( & at_exit_callbacks_ , & it - > queue _) ;
}
@ -157,14 +328,27 @@ void Isolate::Enter() {
}
void Isolate : : Exit ( ) {
NODE_ISOLATE_CHECK ( this ) ;
v8_context_ - > Exit ( ) ;
v8_isolate_ - > Exit ( ) ;
}
void Isolate : : Dispose ( ) {
uv_mutex_lock ( & list_lock ) ;
NODE_ISOLATE_CHECK ( this ) ;
struct AtExitCallbackInfo * it ;
ngx_queue_t * q ;
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 ( ) ;
@ -184,18 +368,117 @@ void Isolate::Dispose() {
}
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 ) {
assert ( uv_thread_self ( ) ! = isolate_ - > tid_ ) ;
NODE_ISOLATE_CHECK ( parent_isolate_ ) ;
HandleScope scope ;
Buffer * buf = Buffer : : New (
msg - > data_ , msg - > size_ , IsolateMessage : : Free , msg ) ;
Handle < Value > argv [ ] = { buf - > handle_ } ;
MakeCallback ( handle_ , " onmessage " , ARRAY_SIZE ( argv ) , 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 = new IsolateMessage ( data , size ) ;
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 ) {
node : : Isolate * isolate = reinterpret_cast < node : : Isolate * > ( arg ) ;
Isolate * isolate = static_cast < Isolate * > ( arg ) ;
isolate - > Enter ( ) ;
// TODO in the future when v0.6 is dead, move StartThread and related
// handles into node_isolate.cc. It is currently organized like this to
// minimize diff (and thus merge conflicts) between the legacy v0.6
// branch.
StartThread ( isolate , isolate - > argc_ , isolate - > argv_ ) ;
isolate - > Dispose ( ) ;
delete isolate ;
}
@ -207,9 +490,9 @@ static Handle<Value> CreateIsolate(const Arguments& args) {
Local < Array > argv = args [ 0 ] . As < Array > ( ) ;
assert ( argv - > Length ( ) > = 2 ) ;
// Note that isolate lock is aquired in the constructor here. It will not
// be unlocked until RunIsolate starts and calls isolate->Enter().
Isolate * isolate = new node : : Isolate ( ) ;
Isolate * current_isolate = node : : Isolate : : GetCurrent ( ) ;
IsolateWrap * wrap = new IsolateWrap ( current_isolate ) ;
Isolate * isolate = wrap - > Get Isolate( ) ;
// Copy over arguments into isolate
isolate - > argc_ = argv - > Length ( ) ;
@ -222,20 +505,10 @@ static Handle<Value> CreateIsolate(const Arguments& args) {
}
isolate - > argv_ [ isolate - > argc_ ] = NULL ;
if ( uv_thread_create ( & isolate - > tid_ , RunIsolate , isolate ) ) {
delete isolate ;
return Null ( ) ;
}
// TODO instead of ObjectTemplate - have a special wrapper.
Local < ObjectTemplate > tpl = ObjectTemplate : : New ( ) ;
tpl - > SetInternalFieldCount ( 2 ) ;
Local < Object > obj = tpl - > NewInstance ( ) ;
obj - > SetPointerInInternalField ( 0 , magic_isolate_cookie_ ) ;
obj - > SetPointerInInternalField ( 1 , isolate ) ;
return scope . Close ( obj ) ;
if ( uv_thread_create ( & isolate - > tid_ , RunIsolate , isolate ) )
return Null ( ) ; // wrap is collected by the GC
else
return wrap - > handle_ ;
}
@ -245,30 +518,10 @@ static Handle<Value> CountIsolate(const Arguments& args) {
}
static Handle < Value > JoinIsolate ( const Arguments & args ) {
HandleScope scope ;
assert ( args [ 0 ] - > IsObject ( ) ) ;
Local < Object > obj = args [ 0 ] - > ToObject ( ) ;
assert ( obj - > InternalFieldCount ( ) = = 2 ) ;
assert ( obj - > GetPointerFromInternalField ( 0 ) = = magic_isolate_cookie_ ) ;
Isolate * ti = reinterpret_cast < Isolate * > (
obj - > GetPointerFromInternalField ( 1 ) ) ;
if ( uv_thread_join ( & ti - > tid_ ) )
return False ( ) ; // error
else
return True ( ) ; // ok
}
void InitIsolates ( Handle < Object > target ) {
HandleScope scope ;
NODE_SET_METHOD ( target , " create " , CreateIsolate ) ;
NODE_SET_METHOD ( target , " count " , CountIsolate ) ;
NODE_SET_METHOD ( target , " join " , JoinIsolate ) ;
}