From 0d5a6b2d9d5070606065eb9e0a7af1e4d7831777 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 22 Nov 2011 16:07:34 +0100 Subject: [PATCH 01/31] uv: upgrade to f5c2a4a --- deps/uv/test/test-list.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 51b847291e..a4c13ad1aa 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -126,6 +126,7 @@ TEST_DECLARE (thread_self) TEST_DECLARE (strlcpy) TEST_DECLARE (strlcat) TEST_DECLARE (counters_init) + #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -292,6 +293,7 @@ TASK_LIST_START TEST_ENTRY (strlcpy) TEST_ENTRY (strlcat) TEST_ENTRY (counters_init) + #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) From c3346f6760f3f4e68a825a0826ebc93c2e150092 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 22 Nov 2011 17:10:09 +0100 Subject: [PATCH 02/31] isolates: isolate-ify the main loop --- node.gyp | 2 ++ src/cares_wrap.cc | 8 ++--- src/fs_event_wrap.cc | 8 ++--- src/handle_wrap.cc | 3 +- src/node.cc | 37 ++++++++++++++---------- src/node.h | 3 ++ src/node_crypto.cc | 5 ++-- src/node_isolate.cc | 42 +++++++++++++++++++++++++++ src/node_isolate.h | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/node_zlib.cc | 2 +- src/pipe_wrap.cc | 8 ++--- src/process_wrap.cc | 6 ++-- src/stream_wrap.cc | 14 ++++----- src/tcp_wrap.cc | 26 ++++++++--------- src/timer_wrap.cc | 18 ++++++------ src/tty_wrap.cc | 6 ++-- src/udp_wrap.cc | 16 +++++----- 17 files changed, 198 insertions(+), 75 deletions(-) create mode 100644 src/node_isolate.cc create mode 100644 src/node_isolate.h diff --git a/node.gyp b/node.gyp index bc35121996..0fff5b4e99 100644 --- a/node.gyp +++ b/node.gyp @@ -74,6 +74,7 @@ 'src/node.cc', 'src/node_vars.cc', 'src/node_buffer.cc', + 'src/node_isolate.cc', 'src/node_constants.cc', 'src/node_extensions.cc', 'src/node_file.cc', @@ -97,6 +98,7 @@ 'src/node.h', 'src/node_vars.h', 'src/node_buffer.h', + 'src/node_isolate.h', 'src/node_constants.h', 'src/node_crypto.h', 'src/node_extensions.h', diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 89ae4581da..976f774c9d 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -607,7 +607,7 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { if (status) { // Error - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); argv[0] = Local::New(Null()); } else { // Success @@ -710,7 +710,7 @@ static Handle GetAddrInfo(const Arguments& args) { hints.ai_family = fam; hints.ai_socktype = SOCK_STREAM; - int r = uv_getaddrinfo(uv_default_loop(), + int r = uv_getaddrinfo(NODE_LOOP(), &req_wrap->req_, AfterGetAddrInfo, *hostname, @@ -719,7 +719,7 @@ static Handle GetAddrInfo(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -736,7 +736,7 @@ static void Initialize(Handle target) { assert(r == ARES_SUCCESS); struct ares_options options; - uv_ares_init_options(uv_default_loop(), &ares_channel, &options, 0); + uv_ares_init_options(NODE_LOOP(), &ares_channel, &options, 0); assert(r == 0); NODE_SET_METHOD(target, "queryA", Query); diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 48cdabc00e..ca584a7938 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -109,15 +109,15 @@ Handle FSEventWrap::Start(const Arguments& args) { String::Utf8Value path(args[0]->ToString()); - int r = uv_fs_event_init(uv_default_loop(), &wrap->handle_, *path, OnEvent, 0); + int r = uv_fs_event_init(NODE_LOOP(), &wrap->handle_, *path, OnEvent, 0); if (r == 0) { // Check for persistent argument if (!args[1]->IsTrue()) { - uv_unref(uv_default_loop()); + uv_unref(NODE_LOOP()); } wrap->initialized_ = true; } else { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } return scope.Close(Integer::New(r)); @@ -145,7 +145,7 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, // assumption that a rename implicitly means an attribute change. Not too // unreasonable, right? Still, we should revisit this before v1.0. if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); eventStr = String::Empty(); } else if (events & UV_RENAME) { diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 5b6594a3a9..bce82bdb05 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -70,7 +70,7 @@ Handle HandleWrap::Unref(const Arguments& args) { } wrap->unref = true; - uv_unref(uv_default_loop()); + uv_unref(NODE_LOOP()); return v8::Undefined(); } @@ -102,7 +102,6 @@ Handle HandleWrap::Close(const Arguments& args) { assert(!wrap->object_.IsEmpty()); uv_close(wrap->handle__, OnClose); - HandleWrap::Ref(args); wrap->StateChange(); diff --git a/src/node.cc b/src/node.cc index f18f754cd3..a3bc129641 100644 --- a/src/node.cc +++ b/src/node.cc @@ -170,7 +170,7 @@ static void Idle(uv_idle_t* watcher, int status) { static void Check(uv_check_t* watcher, int status) { assert(watcher == &gc_check); - tick_times[tick_time_head] = uv_now(uv_default_loop()); + tick_times[tick_time_head] = uv_now(NODE_LOOP()); tick_time_head = (tick_time_head + 1) % RPM_SAMPLES; StartGCTimer(); @@ -200,7 +200,7 @@ static void Tick(void) { need_tick_cb = false; if (uv_is_active((uv_handle_t*) &tick_spinner)) { uv_idle_stop(&tick_spinner); - uv_unref(uv_default_loop()); + uv_unref(NODE_LOOP()); } HandleScope scope; @@ -242,7 +242,7 @@ static Handle NeedTickCallback(const Arguments& args) { // tick_spinner to keep the event loop alive long enough to handle it. if (!uv_is_active((uv_handle_t*) &tick_spinner)) { uv_idle_start(&tick_spinner, Spin); - uv_ref(uv_default_loop()); + uv_ref(NODE_LOOP()); } return Undefined(); } @@ -1494,7 +1494,7 @@ static void CheckStatus(uv_timer_t* watcher, int status) { } } - double d = uv_now(uv_default_loop()) - TICK_TIME(3); + double d = uv_now(NODE_LOOP()) - TICK_TIME(3); //printfb("timer d = %f\n", d); @@ -1523,7 +1523,7 @@ static Handle Uptime(const Arguments& args) { v8::Handle UVCounters(const v8::Arguments& args) { HandleScope scope; - uv_counters_t* c = &uv_default_loop()->counters; + uv_counters_t* c = &NODE_LOOP()->counters; Local obj = Object::New(); @@ -2566,26 +2566,29 @@ char** Init(int argc, char *argv[]) { RegisterSignalHandler(SIGTERM, SignalExit); #endif // __POSIX__ + // Don't use NODE_LOOP(), the node::Isolate() has not yet been initialized. + uv_loop_t* const loop = uv_default_loop(); + uv_prepare_init(uv_default_loop(), &prepare_tick_watcher); uv_prepare_start(&prepare_tick_watcher, PrepareTick); - uv_unref(uv_default_loop()); + uv_unref(loop); uv_check_init(uv_default_loop(), &check_tick_watcher); uv_check_start(&check_tick_watcher, node::CheckTick); - uv_unref(uv_default_loop()); + uv_unref(loop); uv_idle_init(uv_default_loop(), &tick_spinner); - uv_unref(uv_default_loop()); + uv_unref(loop); uv_check_init(uv_default_loop(), &gc_check); uv_check_start(&gc_check, node::Check); - uv_unref(uv_default_loop()); + uv_unref(loop); uv_idle_init(uv_default_loop(), &gc_idle); - uv_unref(uv_default_loop()); + uv_unref(loop); uv_timer_init(uv_default_loop(), &gc_timer); - uv_unref(uv_default_loop()); + uv_unref(loop); V8::SetFatalErrorHandler(node::OnFatalError); @@ -2598,13 +2601,13 @@ char** Init(int argc, char *argv[]) { // main thread to execute a random bit of javascript - which will give V8 // control so it can handle whatever new message had been received on the // debug thread. - uv_async_init(uv_default_loop(), &debug_watcher, node::DebugMessageCallback); + uv_async_init(loop, &debug_watcher, node::DebugMessageCallback); // unref it so that we exit the event loop despite it being active. - uv_unref(uv_default_loop()); + uv_unref(loop); // Fetch a reference to the main isolate, so we have a reference to it // even when we need it to access it from another (debugger) thread. - node_isolate = Isolate::GetCurrent(); + node_isolate = v8::Isolate::GetCurrent(); // If the --debug flag was specified then initialize the debug thread. if (use_debug_agent) { @@ -2646,7 +2649,11 @@ int Start(int argc, char *argv[]) { Persistent context = v8::Context::New(); v8::Context::Scope context_scope(context); + // Create the main node::Isolate object + Isolate::New(uv_default_loop()); + Handle process_l = SetupProcessObject(argc, argv); + v8_typed_array::AttachBindings(context->Global()); // Create all the objects, load modules, do everything. @@ -2658,7 +2665,7 @@ int Start(int argc, char *argv[]) { // there are no watchers on the loop (except for the ones that were // uv_unref'd) then this function exits. As long as there are active // watchers, it blocks. - uv_run(uv_default_loop()); + uv_run(NODE_LOOP()); EmitExit(process_l); diff --git a/src/node.h b/src/node.h index 38d3c621bf..03ae934edf 100644 --- a/src/node.h +++ b/src/node.h @@ -64,6 +64,7 @@ #include #include +#include #include #ifndef offset_of @@ -87,6 +88,8 @@ #define NODE_STRINGIFY_HELPER(n) #n #endif +#define NODE_LOOP() (node::Isolate::GetCurrent()->GetLoop()) + namespace node { int Start(int argc, char *argv[]); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index eb040ebc8c..70ae99d1fb 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4117,7 +4117,8 @@ PBKDF2(const Arguments& args) { req = new uv_work_t(); req->data = request; - uv_queue_work(uv_default_loop(), req, EIO_PBKDF2, EIO_PBKDF2After); + uv_queue_work(NODE_LOOP(), req, EIO_PBKDF2, EIO_PBKDF2After); + return Undefined(); err: @@ -4239,7 +4240,7 @@ Handle RandomBytes(const Arguments& args) { Local callback_v = Local(Function::Cast(*args[1])); req->callback_ = Persistent::New(callback_v); - uv_queue_work(uv_default_loop(), + uv_queue_work(NODE_LOOP(), &req->work_req_, RandomBytesWork, RandomBytesAfter); diff --git a/src/node_isolate.cc b/src/node_isolate.cc new file mode 100644 index 0000000000..af2f83fa8e --- /dev/null +++ b/src/node_isolate.cc @@ -0,0 +1,42 @@ +// 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 "node_isolate.h" +#include + + +namespace node { + + +Isolate* Isolate::New(uv_loop_t* loop) { + return new Isolate(loop); +} + + +Isolate::Isolate(uv_loop_t* loop) { + loop_ = loop; + isolate_ = v8::Isolate::GetCurrent(); + assert(isolate_->GetData() == NULL); + isolate_->SetData(this); +} + + +} // namespace node diff --git a/src/node_isolate.h b/src/node_isolate.h new file mode 100644 index 0000000000..df74a5f01d --- /dev/null +++ b/src/node_isolate.h @@ -0,0 +1,69 @@ +// 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 +#include + +#ifdef NDEBUG +# define NODE_ISOLATE_CHECK(ptr) ((void) (ptr)) +#else +# include +# define NODE_ISOLATE_CHECK(ptr) \ + do { \ + node::Isolate* data_ = node::Isolate::GetCurrent(); \ + assert(data_ == (ptr)); \ + } \ + while (0) +#endif + + +namespace node { + +class Isolate { +public: + static Isolate* New(uv_loop_t* loop); + + static Isolate* GetCurrent() { + return reinterpret_cast(v8::Isolate::GetCurrent()->GetData()); + } + + uv_loop_t* GetLoop() { + NODE_ISOLATE_CHECK(this); + return loop_; + } + + operator v8::Isolate*() { + NODE_ISOLATE_CHECK(this); + return isolate_; + } + +private: + Isolate(uv_loop_t* loop); + v8::Isolate* isolate_; + uv_loop_t* loop_; +}; + +} // namespace node + +#endif // SRC_NODE_ISOLATE_H_ diff --git a/src/node_zlib.cc b/src/node_zlib.cc index e16092f928..dc0bc4691c 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -133,7 +133,7 @@ template class ZCtx : public ObjectWrap { uv_work_t* work_req = new uv_work_t(); work_req->data = req_wrap; - uv_queue_work(uv_default_loop(), + uv_queue_work(NODE_LOOP(), work_req, ZCtx::Process, ZCtx::After); diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 1870837bca..9f66751150 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -123,7 +123,7 @@ Handle PipeWrap::New(const Arguments& args) { PipeWrap::PipeWrap(Handle object, bool ipc) : StreamWrap(object, (uv_stream_t*) &handle_) { - int r = uv_pipe_init(uv_default_loop(), &handle_, ipc); + int r = uv_pipe_init(NODE_LOOP(), &handle_, ipc); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_pipe_init() returns void. handle_.data = reinterpret_cast(this); @@ -141,7 +141,7 @@ Handle PipeWrap::Bind(const Arguments& args) { int r = uv_pipe_bind(&wrap->handle_, *name); // Error starting the pipe. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -172,7 +172,7 @@ Handle PipeWrap::Listen(const Arguments& args) { int r = uv_listen((uv_stream_t*)&wrap->handle_, backlog, OnConnection); // Error starting the pipe. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -225,7 +225,7 @@ void PipeWrap::AfterConnect(uv_connect_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } Local argv[3] = { diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 7a6a8518a2..50c88a9db1 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -175,7 +175,7 @@ class ProcessWrap : public HandleWrap { Get(String::NewSymbol("windowsVerbatimArguments"))->IsTrue(); #endif - int r = uv_spawn(uv_default_loop(), &wrap->process_, options); + int r = uv_spawn(NODE_LOOP(), &wrap->process_, options); wrap->SetHandle((uv_handle_t*)&wrap->process_); assert(wrap->process_.data == wrap); @@ -195,7 +195,7 @@ class ProcessWrap : public HandleWrap { delete [] options.env; } - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -209,7 +209,7 @@ class ProcessWrap : public HandleWrap { int r = uv_process_kill(&wrap->process_, signal); - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 7193f77c12..2e14328472 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -132,7 +132,7 @@ Handle StreamWrap::ReadStart(const Arguments& args) { } // Error starting the tcp. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -146,7 +146,7 @@ Handle StreamWrap::ReadStop(const Arguments& args) { int r = uv_read_stop(wrap->stream_); // Error starting the tcp. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -225,7 +225,7 @@ void StreamWrap::OnReadCommon(uv_stream_t* handle, ssize_t nread, slab_used -= buf.len; } - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); MakeCallback(wrap->object_, "onread", 0, NULL); return; } @@ -338,7 +338,7 @@ Handle StreamWrap::Write(const Arguments& args) { wrap->UpdateWriteQueueSize(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -358,7 +358,7 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } wrap->UpdateWriteQueueSize(); @@ -388,7 +388,7 @@ Handle StreamWrap::Shutdown(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -408,7 +408,7 @@ void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { HandleScope scope; if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } Local argv[3] = { diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 9ac6aedfa3..2197850d16 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -154,7 +154,7 @@ Handle TCPWrap::New(const Arguments& args) { TCPWrap::TCPWrap(Handle object) : StreamWrap(object, (uv_stream_t*) &handle_) { - int r = uv_tcp_init(uv_default_loop(), &handle_); + int r = uv_tcp_init(NODE_LOOP(), &handle_); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_tcp_init() returns void. UpdateWriteQueueSize(); @@ -182,7 +182,7 @@ Handle TCPWrap::GetSockName(const Arguments& args) { Local sockname = Object::New(); if (r != 0) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } else { family = address.ss_family; @@ -224,7 +224,7 @@ Handle TCPWrap::GetPeerName(const Arguments& args) { Local sockname = Object::New(); if (r != 0) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } else { family = address.ss_family; @@ -257,7 +257,7 @@ Handle TCPWrap::SetNoDelay(const Arguments& args) { int r = uv_tcp_nodelay(&wrap->handle_, 1); if (r) - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return Undefined(); } @@ -273,7 +273,7 @@ Handle TCPWrap::SetKeepAlive(const Arguments& args) { int r = uv_tcp_keepalive(&wrap->handle_, enable, delay); if (r) - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return Undefined(); } @@ -289,7 +289,7 @@ Handle TCPWrap::SetSimultaneousAccepts(const Arguments& args) { int r = uv_tcp_simultaneous_accepts(&wrap->handle_, enable ? 1 : 0); if (r) - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return Undefined(); } @@ -308,7 +308,7 @@ Handle TCPWrap::Bind(const Arguments& args) { int r = uv_tcp_bind(&wrap->handle_, address); // Error starting the tcp. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -326,7 +326,7 @@ Handle TCPWrap::Bind6(const Arguments& args) { int r = uv_tcp_bind6(&wrap->handle_, address); // Error starting the tcp. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -342,7 +342,7 @@ Handle TCPWrap::Listen(const Arguments& args) { int r = uv_listen((uv_stream_t*)&wrap->handle_, backlog, OnConnection); // Error starting the tcp. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -377,7 +377,7 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) { // Successful accept. Call the onconnection callback in JavaScript land. argv[0] = client_obj; } else { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); argv[0] = v8::Null(); } @@ -396,7 +396,7 @@ void TCPWrap::AfterConnect(uv_connect_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } Local argv[3] = { @@ -432,7 +432,7 @@ Handle TCPWrap::Connect(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -459,7 +459,7 @@ Handle TCPWrap::Connect6(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return scope.Close(v8::Null()); } else { diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 470c2d6b98..fb86036923 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -91,7 +91,7 @@ class TimerWrap : public HandleWrap { : HandleWrap(object, (uv_handle_t*) &handle_) { active_ = false; - int r = uv_timer_init(uv_default_loop(), &handle_); + int r = uv_timer_init(NODE_LOOP(), &handle_); assert(r == 0); handle_.data = this; @@ -99,11 +99,11 @@ class TimerWrap : public HandleWrap { // uv_timer_init adds a loop reference. (That is, it calls uv_ref.) This // is not the behavior we want in Node. Timers should not increase the // ref count of the loop except when active. - uv_unref(uv_default_loop()); + uv_unref(NODE_LOOP()); } ~TimerWrap() { - if (!active_) uv_ref(uv_default_loop()); + if (!active_) uv_ref(NODE_LOOP()); } void StateChange() { @@ -113,11 +113,11 @@ class TimerWrap : public HandleWrap { if (!was_active && active_) { // If our state is changing from inactive to active, we // increase the loop's reference count. - uv_ref(uv_default_loop()); + uv_ref(NODE_LOOP()); } else if (was_active && !active_) { // If our state is changing from active to inactive, we // decrease the loop's reference count. - uv_unref(uv_default_loop()); + uv_unref(NODE_LOOP()); } } @@ -132,7 +132,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_start(&wrap->handle_, OnTimeout, timeout, repeat); // Error starting the timer. - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); wrap->StateChange(); @@ -146,7 +146,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_stop(&wrap->handle_); - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); wrap->StateChange(); @@ -160,7 +160,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_again(&wrap->handle_); - if (r) SetErrno(uv_last_error(uv_default_loop())); + if (r) SetErrno(uv_last_error(NODE_LOOP())); wrap->StateChange(); @@ -186,7 +186,7 @@ class TimerWrap : public HandleWrap { int64_t repeat = uv_timer_get_repeat(&wrap->handle_); - if (repeat < 0) SetErrno(uv_last_error(uv_default_loop())); + if (repeat < 0) SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(repeat)); } diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 486c2a70b4..b3ed49fa84 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -124,7 +124,7 @@ class TTYWrap : StreamWrap { int r = uv_tty_get_winsize(&wrap->handle_, &width, &height); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return v8::Undefined(); } @@ -143,7 +143,7 @@ class TTYWrap : StreamWrap { int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue()); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } return scope.Close(Integer::New(r)); @@ -169,7 +169,7 @@ class TTYWrap : StreamWrap { TTYWrap(Handle object, int fd, bool readable) : StreamWrap(object, (uv_stream_t*)&handle_) { - uv_tty_init(uv_default_loop(), &handle_, fd, readable); + uv_tty_init(NODE_LOOP(), &handle_, fd, readable); } uv_tty_t handle_; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index cd4c58ecab..81405f4f69 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -103,7 +103,7 @@ private: UDPWrap::UDPWrap(Handle object): HandleWrap(object, (uv_handle_t*)&handle_) { - int r = uv_udp_init(uv_default_loop(), &handle_); + int r = uv_udp_init(NODE_LOOP(), &handle_); assert(r == 0); // can't fail anyway handle_.data = reinterpret_cast(this); } @@ -176,7 +176,7 @@ Handle UDPWrap::DoBind(const Arguments& args, int family) { } if (r) - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return scope.Close(Integer::New(r)); } @@ -233,7 +233,7 @@ Handle UDPWrap::DoSend(const Arguments& args, int family) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); delete req_wrap; return Null(); } @@ -260,8 +260,8 @@ Handle UDPWrap::RecvStart(const Arguments& args) { // UV_EALREADY means that the socket is already bound but that's okay int r = uv_udp_recv_start(&wrap->handle_, OnAlloc, OnRecv); - if (r && uv_last_error(uv_default_loop()).code != UV_EALREADY) { - SetErrno(uv_last_error(uv_default_loop())); + if (r && uv_last_error(NODE_LOOP()).code != UV_EALREADY) { + SetErrno(uv_last_error(NODE_LOOP())); return False(); } @@ -297,7 +297,7 @@ Handle UDPWrap::GetSockName(const Arguments& args) { return scope.Close(sockname); } else { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); return Null(); } } @@ -316,7 +316,7 @@ void UDPWrap::OnSend(uv_udp_send_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } Local argv[4] = { @@ -364,7 +364,7 @@ void UDPWrap::OnRecv(uv_udp_t* handle, }; if (nread == -1) { - SetErrno(uv_last_error(uv_default_loop())); + SetErrno(uv_last_error(NODE_LOOP())); } else { Local rinfo = Object::New(); From 2a7a2ca986a846630726602b94dcebe657ee2efa Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 23 Nov 2011 20:50:28 +0100 Subject: [PATCH 03/31] isolates: add atexit() functionality for isolates --- src/node.cc | 4 +- src/node_isolate.cc | 28 ++ src/node_isolate.h | 23 +- src/queue.h | 741 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 793 insertions(+), 3 deletions(-) create mode 100644 src/queue.h diff --git a/src/node.cc b/src/node.cc index a3bc129641..e80fec8f82 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2650,7 +2650,7 @@ int Start(int argc, char *argv[]) { v8::Context::Scope context_scope(context); // Create the main node::Isolate object - Isolate::New(uv_default_loop()); + Isolate* isolate = Isolate::New(uv_default_loop()); Handle process_l = SetupProcessObject(argc, argv); @@ -2669,6 +2669,8 @@ int Start(int argc, char *argv[]) { EmitExit(process_l); + isolate->Dispose(); + #ifndef NDEBUG // Clean up. context.Dispose(); diff --git a/src/node_isolate.cc b/src/node_isolate.cc index af2f83fa8e..1255850d6f 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -20,6 +20,9 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node_isolate.h" + +#include +#include #include @@ -34,9 +37,34 @@ Isolate* Isolate::New(uv_loop_t* loop) { Isolate::Isolate(uv_loop_t* loop) { loop_ = loop; isolate_ = v8::Isolate::GetCurrent(); + SLIST_INIT(&at_exit_callbacks_); assert(isolate_->GetData() == NULL); isolate_->SetData(this); } +void Isolate::AtExit(AtExitCallback callback, void* arg) { + struct AtExitCallbackInfo* it = new AtExitCallbackInfo; + + NODE_ISOLATE_CHECK(this); + + it->callback_ = callback; + it->arg_ = arg; + + SLIST_INSERT_HEAD(&at_exit_callbacks_, it, entries_); +} + + +void Isolate::Dispose() { + struct AtExitCallbackInfo* it; + + NODE_ISOLATE_CHECK(this); + + SLIST_FOREACH(it, &at_exit_callbacks_, entries_) { + it->callback_(it->arg_); + delete it; + } +} + + } // namespace node diff --git a/src/node_isolate.h b/src/node_isolate.h index df74a5f01d..415f69525f 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -22,8 +22,9 @@ #ifndef SRC_NODE_ISOLATE_H_ #define SRC_NODE_ISOLATE_H_ -#include -#include +#include "queue.h" +#include "v8.h" +#include "uv.h" #ifdef NDEBUG # define NODE_ISOLATE_CHECK(ptr) ((void) (ptr)) @@ -42,6 +43,8 @@ namespace node { class Isolate { public: + typedef void (*AtExitCallback)(void* arg); + static Isolate* New(uv_loop_t* loop); static Isolate* GetCurrent() { @@ -58,8 +61,24 @@ public: return isolate_; } + /* Register a handler that should run when the current isolate exits. + * Handlers run in LIFO order. + */ + void AtExit(AtExitCallback callback, void *arg); + + /* Shutdown the isolate. Call this method at thread death. */ + void Dispose(); + private: Isolate(uv_loop_t* loop); + + struct AtExitCallbackInfo { + SLIST_ENTRY(AtExitCallbackInfo) entries_; + AtExitCallback callback_; + void* arg_; + }; + + SLIST_HEAD(AtExitCallbacks, AtExitCallbackInfo) at_exit_callbacks_; v8::Isolate* isolate_; uv_loop_t* loop_; }; diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000000..ea2b7b4b17 --- /dev/null +++ b/src/queue.h @@ -0,0 +1,741 @@ +/* $NetBSD: queue.h,v 1.53 2011/11/19 22:51:31 tls Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) && ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_LAST(head, type, field) \ + (SIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != NULL && ((next) = TAILQ_NEXT(var, field), 1); \ + (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((prev) = TAILQ_PREV((var), headname, field), 1);\ + (var) = (prev)) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ + if ((head)->cqh_first != (void *)(head) && \ + (head)->cqh_first->field.cqe_prev != (void *)(head)) \ + panic("CIRCLEQ head forw %p %s:%d", (head), \ + __FILE__, __LINE__); \ + if ((head)->cqh_last != (void *)(head) && \ + (head)->cqh_last->field.cqe_next != (void *)(head)) \ + panic("CIRCLEQ head back %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ + if ((elm)->field.cqe_next == (void *)(head)) { \ + if ((head)->cqh_last != (elm)) \ + panic("CIRCLEQ elm last %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ + panic("CIRCLEQ elm forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } \ + if ((elm)->field.cqe_prev == (void *)(head)) { \ + if ((head)->cqh_first != (elm)) \ + panic("CIRCLEQ elm first %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ + panic("CIRCLEQ elm prev %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ + (elm)->field.cqe_next = (void *)1L; \ + (elm)->field.cqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) +#endif + +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#endif /* !_SYS_QUEUE_H_ */ From 39e2c469db86c4cdcef901dd619cdeb5a3e62065 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 29 Nov 2011 16:41:41 +0100 Subject: [PATCH 04/31] build: add --without-isolates configure switch --- configure | 6 ++++++ node.gyp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/configure b/configure index dbe7f243e1..401b370e92 100755 --- a/configure +++ b/configure @@ -27,6 +27,11 @@ parser.add_option("--without-npm", dest="without_npm", help="Don\'t install the bundled npm package manager") +parser.add_option("--without-isolates", + action="store_true", + dest="without_isolates", + help="Build without isolates (no threads, single loop) [Default: False]") + parser.add_option("--without-ssl", action="store_true", dest="without_ssl", @@ -163,6 +168,7 @@ def target_arch(): def configure_node(o): # TODO add gdb and dest_cpu + o['variables']['node_use_isolates'] = b(not options.without_isolates) o['variables']['node_debug'] = b(options.debug) o['variables']['node_prefix'] = options.prefix if options.prefix else '' o['variables']['node_use_dtrace'] = b(options.with_dtrace) diff --git a/node.gyp b/node.gyp index 0fff5b4e99..9de3492834 100644 --- a/node.gyp +++ b/node.gyp @@ -130,6 +130,12 @@ ], 'conditions': [ + [ 'node_use_isolates=="true"', { + 'defines': [ 'HAVE_ISOLATES=1' ], + }, { + 'defines': [ 'HAVE_ISOLATES=0' ], + }], + [ 'node_use_openssl=="true"', { 'defines': [ 'HAVE_OPENSSL=1' ], 'sources': [ 'src/node_crypto.cc' ], From a778a25cabdef7a94c34a315706568688d942028 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 29 Nov 2011 17:35:19 +0100 Subject: [PATCH 05/31] build: move internals to separate header file --- node.gyp | 1 + src/node.h | 16 +------ src/node_internals.h | 99 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 src/node_internals.h diff --git a/node.gyp b/node.gyp index 9de3492834..b4a3e461fa 100644 --- a/node.gyp +++ b/node.gyp @@ -125,6 +125,7 @@ ], 'defines': [ + 'NODE_WANT_INTERNALS=1', 'ARCH="<(target_arch)"', 'PLATFORM="<(OS)"', ], diff --git a/src/node.h b/src/node.h index 03ae934edf..e0df48e88f 100644 --- a/src/node.h +++ b/src/node.h @@ -67,20 +67,8 @@ #include #include -#ifndef offset_of -// g++ in strict mode complains loudly about the system offsetof() macro -// because it uses NULL as the base address. -#define offset_of(type, member) \ - ((intptr_t) ((char *) &(((type *) 8)->member) - 8)) -#endif - -#ifndef container_of -#define container_of(ptr, type, member) \ - ((type *) ((char *) (ptr) - offset_of(type, member))) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#if NODE_WANT_INTERNALS +# include "node_internals.h" #endif #ifndef NODE_STRINGIFY diff --git a/src/node_internals.h b/src/node_internals.h new file mode 100644 index 0000000000..8088e229ec --- /dev/null +++ b/src/node_internals.h @@ -0,0 +1,99 @@ +// 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_INTERNALS_H_ +#define SRC_NODE_INTERNALS_H_ + +namespace node { + +#ifndef offset_of +// g++ in strict mode complains loudly about the system offsetof() macro +// because it uses NULL as the base address. +#define offset_of(type, member) \ + ((intptr_t) ((char *) &(((type *) 8)->member) - 8)) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offset_of(type, member))) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) +#endif + +// +// isolates support +// +#if HAVE_ISOLATES + +# if _WIN32 +# define THREAD __declspec(thread) +# else +# define THREAD __thread +# endif + +# define TLS(type, name) THREAD type* __tls_##name +# define VAR(name) (*__tls_##name) +# define EMPTY(name) (__tls_##name == NULL) +# define ASSIGN(name, val) ((__tls_##name) = P(val)) + +# define LAZY_ASSIGN(name, val) \ + do if (!__tls_##name) ((__tls_##name) = P(val)); while (0) + +template inline v8::Persistent* P(v8::Handle v) +{ + return new v8::Persistent(v8::Persistent::New(v)); +} + +inline v8::Persistent* P(const char* symbol) +{ + return new v8::Persistent( + v8::Persistent::New( + v8::String::NewSymbol(symbol))); +} + +#else // !HAVE_ISOLATES + +# define THREAD /* nothing */ +# define TLS(type, name) type name +# define VAR(name) (name) +# define EMPTY(name) ((name).IsEmpty()) +# define ASSIGN(name, val) ((name) = P(val)) + +# define LAZY_ASSIGN(name, val) \ + do if ((name).IsEmpty()) (name) = P(val); while (0) + +template inline v8::Persistent P(v8::Handle v) +{ + return v8::Persistent(v); +} + +inline v8::Persistent P(const char* symbol) +{ + return v8::Persistent::New( + v8::String::NewSymbol(symbol)); +} +#endif // HAVE_ISOLATES + +} // namespace node + +#endif // SRC_NODE_INTERNALS_H_ From edbec3f8f3e8f8239a9b1c9a9a0a229efcdb4938 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 23 Nov 2011 21:46:22 +0100 Subject: [PATCH 06/31] isolates: add _newIsolate() and _joinIsolate() to process object --- src/node.cc | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/node_isolate.cc | 8 +++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/node.cc b/src/node.cc index e80fec8f82..6352583e3c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1847,6 +1847,52 @@ static Handle Binding(const Arguments& args) { } +static void RunIsolate(void* arg) { + uv_loop_t* loop = uv_loop_new(); + Isolate* isolate = Isolate::New(loop); +} + + +static char magic_isolate_cookie_[] = "magic isolate cookie"; + + +static Handle NewIsolate(const Arguments& args) { + HandleScope scope; + + uv_thread_t* tid = new uv_thread_t; + + if (uv_thread_create(tid, RunIsolate, NULL)) + return Null(); + + Local tpl = ObjectTemplate::New(); + tpl->SetInternalFieldCount(2); + + Local obj = tpl->NewInstance(); + obj->SetPointerInInternalField(0, magic_isolate_cookie_); + obj->SetPointerInInternalField(1, tid); + + return scope.Close(obj); +} + + +static Handle JoinIsolate(const Arguments& args) { + HandleScope scope; + + assert(args[0]->IsObject()); + + Local obj = args[0]->ToObject(); + assert(obj->InternalFieldCount() == 2); + assert(obj->GetPointerFromInternalField(0) == magic_isolate_cookie_); + + uv_thread_t* tid = (uv_thread_t*) obj->GetPointerFromInternalField(1); + + if (uv_thread_join(tid)) + return False(); // error + else + return True(); // ok +} + + static Handle ProcessTitleGetter(Local property, const AccessorInfo& info) { HandleScope scope; @@ -2119,6 +2165,9 @@ Handle SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "binding", Binding); + NODE_SET_METHOD(process, "_newIsolate", NewIsolate); + NODE_SET_METHOD(process, "_joinIsolate", JoinIsolate); + return process; } diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 1255850d6f..808a87265f 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -35,9 +35,15 @@ Isolate* Isolate::New(uv_loop_t* loop) { Isolate::Isolate(uv_loop_t* loop) { + SLIST_INIT(&at_exit_callbacks_); loop_ = loop; + isolate_ = v8::Isolate::GetCurrent(); - SLIST_INIT(&at_exit_callbacks_); + if (isolate_ == NULL) { + isolate_ = v8::Isolate::New(); + isolate_->Enter(); + } + assert(isolate_->GetData() == NULL); isolate_->SetData(this); } From 9caeceef6dd497d922330a281fbae021a6ef200d Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 8 Dec 2011 22:44:10 -0800 Subject: [PATCH 07/31] isolates have globals stored in struct globals --- src/node_isolate.cc | 7 +++++++ src/node_isolate.h | 6 ++++++ src/node_vars.cc | 17 ++++++++++++----- src/node_vars.h | 5 +++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 808a87265f..50d6deda0e 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -46,6 +46,13 @@ Isolate::Isolate(uv_loop_t* loop) { assert(isolate_->GetData() == NULL); isolate_->SetData(this); + + globals_init(&globals_); +} + + +struct globals* Isolate::Globals() { + return &globals_; } diff --git a/src/node_isolate.h b/src/node_isolate.h index 415f69525f..5fe8db9086 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -25,6 +25,7 @@ #include "queue.h" #include "v8.h" #include "uv.h" +#include "node_vars.h" #ifdef NDEBUG # define NODE_ISOLATE_CHECK(ptr) ((void) (ptr)) @@ -69,6 +70,8 @@ public: /* Shutdown the isolate. Call this method at thread death. */ void Dispose(); + struct globals* Globals(); + private: Isolate(uv_loop_t* loop); @@ -81,6 +84,9 @@ private: SLIST_HEAD(AtExitCallbacks, AtExitCallbackInfo) at_exit_callbacks_; v8::Isolate* isolate_; uv_loop_t* loop_; + + // Global variables for this isolate. + struct globals globals_; }; } // namespace node diff --git a/src/node_vars.cc b/src/node_vars.cc index 28718042d0..e462752f7d 100644 --- a/src/node_vars.cc +++ b/src/node_vars.cc @@ -1,4 +1,5 @@ #include +#include #if HAVE_OPENSSL # include #endif @@ -9,11 +10,7 @@ namespace node { // For now we just statically initialize the globals structure. Later there // will be one struct globals for each isolate. -static struct globals g_struct; -static struct globals* g_ptr; - - -static void globals_init(struct globals* g) { +void globals_init(struct globals* g) { memset(g, 0, sizeof(struct globals)); g->debug_port = 5858; @@ -31,6 +28,15 @@ static void globals_init(struct globals* g) { } +#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; @@ -38,5 +44,6 @@ struct globals* globals_get() { } return g_ptr; } +#endif // HAVE_ISOLATES } // namespace node diff --git a/src/node_vars.h b/src/node_vars.h index 9b2dd8e8bc..f124148cfc 100644 --- a/src/node_vars.h +++ b/src/node_vars.h @@ -183,6 +183,11 @@ struct globals { ::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 From 8a538ce372c49b23af7582560cf40ba2c2637691 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Dec 2011 18:49:17 +0100 Subject: [PATCH 08/31] Remove per-process globals from per-thread context. --- src/node.cc | 17 ++++++++--------- src/node_vars.cc | 1 - src/node_vars.h | 7 ------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/node.cc b/src/node.cc index 6352583e3c..a192da432f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -97,12 +97,9 @@ extern char **environ; // use the variables as they were being used before. #define check_tick_watcher NODE_VAR(check_tick_watcher) #define code_symbol NODE_VAR(code_symbol) -#define debug_port NODE_VAR(debug_port) -#define debug_wait_connect NODE_VAR(debug_wait_connect) #define emit_symbol NODE_VAR(emit_symbol) #define errno_symbol NODE_VAR(errno_symbol) #define errpath_symbol NODE_VAR(errpath_symbol) -#define eval_string NODE_VAR(eval_string) #define gc_check NODE_VAR(gc_check) #define gc_idle NODE_VAR(gc_idle) #define gc_timer NODE_VAR(gc_timer) @@ -110,11 +107,8 @@ extern char **environ; #define heap_total_symbol NODE_VAR(heap_total_symbol) #define heap_used_symbol NODE_VAR(heap_used_symbol) #define listeners_symbol NODE_VAR(listeners_symbol) -#define max_stack_size NODE_VAR(max_stack_size) #define need_tick_cb NODE_VAR(need_tick_cb) -#define option_end_index NODE_VAR(option_end_index) #define prepare_tick_watcher NODE_VAR(prepare_tick_watcher) -#define print_eval NODE_VAR(print_eval) #define process NODE_VAR(process) #define rss_symbol NODE_VAR(rss_symbol) #define syscall_symbol NODE_VAR(syscall_symbol) @@ -123,7 +117,6 @@ extern char **environ; #define tick_time_head NODE_VAR(tick_time_head) #define tick_times NODE_VAR(tick_times) #define uncaught_exception_symbol NODE_VAR(uncaught_exception_symbol) -#define use_debug_agent NODE_VAR(use_debug_agent) #define use_npn NODE_VAR(use_npn) #define use_sni NODE_VAR(use_sni) #define uncaught_exception_counter NODE_VAR(uncaught_exception_counter) @@ -136,11 +129,17 @@ extern char **environ; namespace node { +static int option_end_index; +static unsigned long max_stack_size; +static unsigned short debug_port = 5858; +static bool debug_wait_connect; +static bool use_debug_agent; +static const char* eval_string; +static bool print_eval; -#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES] - +#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES] static void CheckStatus(uv_timer_t* watcher, int status); diff --git a/src/node_vars.cc b/src/node_vars.cc index e462752f7d..01bf4067e5 100644 --- a/src/node_vars.cc +++ b/src/node_vars.cc @@ -12,7 +12,6 @@ namespace node { void globals_init(struct globals* g) { memset(g, 0, sizeof(struct globals)); - g->debug_port = 5858; #ifdef OPENSSL_NPN_NEGOTIATED g->use_npn = true; diff --git a/src/node_vars.h b/src/node_vars.h index f124148cfc..dbd362bb90 100644 --- a/src/node_vars.h +++ b/src/node_vars.h @@ -31,13 +31,6 @@ struct globals { v8::Persistent listeners_symbol; v8::Persistent uncaught_exception_symbol; v8::Persistent emit_symbol; - bool print_eval; - char *eval_string; - int option_end_index; - bool use_debug_agent; - bool debug_wait_connect; - int debug_port; - int max_stack_size; uv_check_t check_tick_watcher; uv_prepare_t prepare_tick_watcher; uv_idle_t tick_spinner; From 3aadd4fe7678b35f0f4df55833bef081ec7b3ef5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Dec 2011 19:02:33 +0100 Subject: [PATCH 09/31] Separate per-process and per-thread init logic. --- src/node.cc | 86 +++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/node.cc b/src/node.cc index a192da432f..36987d76aa 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2365,6 +2365,7 @@ static void EnableDebug(bool wait_connect) { #ifdef __POSIX__ +// FIXME this is positively unsafe with isolates/threads static void EnableDebugSignalHandler(int signal) { // Break once process will return execution to v8 v8::Debug::DebugBreak(node_isolate); @@ -2567,7 +2568,7 @@ static Handle DebugPause(const Arguments& args) { } -char** Init(int argc, char *argv[]) { +char** ProcessInit(int argc, char *argv[]) { // Initialize prog_start_time to get relative uptime. uv_uptime(&prog_start_time); @@ -2610,37 +2611,55 @@ char** Init(int argc, char *argv[]) { #ifdef __POSIX__ // Ignore SIGPIPE RegisterSignalHandler(SIGPIPE, SIG_IGN); + // TODO decide whether to handle these signals per-process or per-thread RegisterSignalHandler(SIGINT, SignalExit); RegisterSignalHandler(SIGTERM, SignalExit); #endif // __POSIX__ - // Don't use NODE_LOOP(), the node::Isolate() has not yet been initialized. - uv_loop_t* const loop = uv_default_loop(); + return argv; +} + + +void EmitExit(v8::Handle process_l) { + // process.emit('exit') + Local emit_v = process_l->Get(String::New("emit")); + assert(emit_v->IsFunction()); + Local emit = Local::Cast(emit_v); + Local args[] = { String::New("exit") }; + TryCatch try_catch; + emit->Call(process_l, 1, args); + if (try_catch.HasCaught()) { + FatalException(try_catch); + } +} + - uv_prepare_init(uv_default_loop(), &prepare_tick_watcher); +void StartThread(Isolate* isolate, int argc, char** argv) { + uv_loop_t* loop = isolate->GetLoop(); + + uv_prepare_init(loop, &prepare_tick_watcher); uv_prepare_start(&prepare_tick_watcher, PrepareTick); uv_unref(loop); - uv_check_init(uv_default_loop(), &check_tick_watcher); + uv_check_init(loop, &check_tick_watcher); uv_check_start(&check_tick_watcher, node::CheckTick); uv_unref(loop); - uv_idle_init(uv_default_loop(), &tick_spinner); + uv_idle_init(loop, &tick_spinner); uv_unref(loop); - uv_check_init(uv_default_loop(), &gc_check); + uv_check_init(loop, &gc_check); uv_check_start(&gc_check, node::Check); uv_unref(loop); - uv_idle_init(uv_default_loop(), &gc_idle); + uv_idle_init(loop, &gc_idle); uv_unref(loop); - uv_timer_init(uv_default_loop(), &gc_timer); + uv_timer_init(loop, &gc_timer); uv_unref(loop); V8::SetFatalErrorHandler(node::OnFatalError); - // Set the callback DebugMessageDispatch which is called from the debug // thread. v8::Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch); @@ -2668,27 +2687,27 @@ char** Init(int argc, char *argv[]) { #endif // __POSIX__ } - return argv; -} + Handle process_l = SetupProcessObject(argc, argv); + v8_typed_array::AttachBindings(v8::Context::GetCurrent()->Global()); + // Create all the objects, load modules, do everything. + // so your next reading stop should be node::Load()! + Load(process_l); -void EmitExit(v8::Handle process_l) { - // process.emit('exit') - Local emit_v = process_l->Get(String::New("emit")); - assert(emit_v->IsFunction()); - Local emit = Local::Cast(emit_v); - Local args[] = { String::New("exit") }; - TryCatch try_catch; - emit->Call(process_l, 1, args); - if (try_catch.HasCaught()) { - FatalException(try_catch); - } + // All our arguments are loaded. We've evaluated all of the scripts. We + // might even have created TCP servers. Now we enter the main eventloop. If + // there are no watchers on the loop (except for the ones that were + // uv_unref'd) then this function exits. As long as there are active + // watchers, it blocks. + uv_run(loop); + + EmitExit(process_l); } int Start(int argc, char *argv[]) { // This needs to run *before* V8::Initialize() - argv = Init(argc, argv); + argv = ProcessInit(argc, argv); v8::V8::Initialize(); v8::HandleScope handle_scope; @@ -2699,24 +2718,7 @@ int Start(int argc, char *argv[]) { // Create the main node::Isolate object Isolate* isolate = Isolate::New(uv_default_loop()); - - Handle process_l = SetupProcessObject(argc, argv); - - v8_typed_array::AttachBindings(context->Global()); - - // Create all the objects, load modules, do everything. - // so your next reading stop should be node::Load()! - Load(process_l); - - // All our arguments are loaded. We've evaluated all of the scripts. We - // might even have created TCP servers. Now we enter the main eventloop. If - // there are no watchers on the loop (except for the ones that were - // uv_unref'd) then this function exits. As long as there are active - // watchers, it blocks. - uv_run(NODE_LOOP()); - - EmitExit(process_l); - + StartThread(isolate, argc, argv); isolate->Dispose(); #ifndef NDEBUG From 9d71e74491081f2682224ce0b92079faf2f97782 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Dec 2011 21:12:48 +0100 Subject: [PATCH 10/31] isolates: rename node::Isolate member isolate_ to v8_isolate_ --- src/node_isolate.cc | 12 ++++++------ src/node_isolate.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 50d6deda0e..b6156fdfcd 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -38,14 +38,14 @@ Isolate::Isolate(uv_loop_t* loop) { SLIST_INIT(&at_exit_callbacks_); loop_ = loop; - isolate_ = v8::Isolate::GetCurrent(); - if (isolate_ == NULL) { - isolate_ = v8::Isolate::New(); - isolate_->Enter(); + v8_isolate_ = v8::Isolate::GetCurrent(); + if (v8_isolate_ == NULL) { + v8_isolate_ = v8::Isolate::New(); + v8_isolate_->Enter(); } - assert(isolate_->GetData() == NULL); - isolate_->SetData(this); + assert(v8_isolate_->GetData() == NULL); + v8_isolate_->SetData(this); globals_init(&globals_); } diff --git a/src/node_isolate.h b/src/node_isolate.h index 5fe8db9086..e24bde1f85 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -57,9 +57,9 @@ public: return loop_; } - operator v8::Isolate*() { + v8::Isolate* GetV8Isolate() { NODE_ISOLATE_CHECK(this); - return isolate_; + return v8_isolate_; } /* Register a handler that should run when the current isolate exits. @@ -82,7 +82,7 @@ private: }; SLIST_HEAD(AtExitCallbacks, AtExitCallbackInfo) at_exit_callbacks_; - v8::Isolate* isolate_; + v8::Isolate* v8_isolate_; uv_loop_t* loop_; // Global variables for this isolate. From 3063ba080082ed5f32655b3b61b9ca06842dec31 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Dec 2011 21:14:00 +0100 Subject: [PATCH 11/31] isolates: have node::Isolate manage the v8::Context --- src/node_isolate.cc | 12 ++++++++++++ src/node_isolate.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/node_isolate.cc b/src/node_isolate.cc index b6156fdfcd..fcf5db7fd5 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -47,6 +47,9 @@ Isolate::Isolate(uv_loop_t* loop) { assert(v8_isolate_->GetData() == NULL); v8_isolate_->SetData(this); + v8_context_ = v8::Context::New(); + v8_context_->Enter(); + globals_init(&globals_); } @@ -77,6 +80,15 @@ void Isolate::Dispose() { 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; } diff --git a/src/node_isolate.h b/src/node_isolate.h index e24bde1f85..74fe7866ba 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -62,6 +62,11 @@ public: return v8_isolate_; } + v8::Handle GetV8Context() { + NODE_ISOLATE_CHECK(this); + return v8_context_; + } + /* Register a handler that should run when the current isolate exits. * Handlers run in LIFO order. */ @@ -82,6 +87,7 @@ private: }; SLIST_HEAD(AtExitCallbacks, AtExitCallbackInfo) at_exit_callbacks_; + v8::Persistent v8_context_; v8::Isolate* v8_isolate_; uv_loop_t* loop_; From d329fc7b09890876030542c53f932a510219bc0b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 9 Dec 2011 21:49:10 +0100 Subject: [PATCH 12/31] isolates: add process.tid property, pass args to isolate --- src/node.cc | 121 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 103 insertions(+), 18 deletions(-) diff --git a/src/node.cc b/src/node.cc index 36987d76aa..307ecf5df8 100644 --- a/src/node.cc +++ b/src/node.cc @@ -129,6 +129,8 @@ extern char **environ; namespace node { +#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES] + static int option_end_index; static unsigned long max_stack_size; static unsigned short debug_port = 5858; @@ -137,11 +139,14 @@ static bool use_debug_agent; static const char* eval_string; static bool print_eval; +static void CheckStatus(uv_timer_t* watcher, int status); +static unsigned long NewThreadId(); +void StartThread(unsigned long thread_id, + Isolate* isolate, + int argc, + char** argv); -#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES] - -static void CheckStatus(uv_timer_t* watcher, int status); static void StartGCTimer () { if (!uv_is_active((uv_handle_t*) &gc_timer)) { @@ -1846,9 +1851,71 @@ static Handle Binding(const Arguments& args) { } +static struct { + uv_mutex_t lock_; + unsigned long counter_; +} thread_id_generator_; + + +static unsigned long NewThreadId() { + unsigned long thread_id; + + uv_mutex_lock(&thread_id_generator_.lock_); + thread_id = ++thread_id_generator_.counter_; + uv_mutex_unlock(&thread_id_generator_.lock_); + + return thread_id; +} + + +struct ThreadInfo { + unsigned long thread_id_; + uv_thread_t thread_; + char** argv_; + int argc_; + + ThreadInfo(int argc, char** argv) { + argc_ = argc; + argv_ = new char*[argc_ + 1]; + + for (int i = 0; i < argc_; ++i) { + size_t size = 1 + strlen(argv[i]); + argv_[i] = new char[size]; + memcpy(argv_[i], argv[i], size); + } + argv_[argc_] = NULL; + } + + ThreadInfo(Handle args) { + argc_ = args->Length(); + argv_ = new char*[argc_ + 1]; + + for (int i = 0; i < argc_; ++i) { + String::Utf8Value str(args->Get(i)); + size_t size = 1 + strlen(*str); + argv_[i] = new char[size]; + memcpy(argv_[i], *str, size); + } + argv_[argc_] = NULL; + } + + ~ThreadInfo() { + for (int i = 0; i < argc_; ++i) { + delete[] argv_[i]; + } + delete argv_; + } +}; + + static void RunIsolate(void* arg) { + ThreadInfo* ti = reinterpret_cast(arg); + uv_loop_t* loop = uv_loop_new(); Isolate* isolate = Isolate::New(loop); + + StartThread(ti->thread_id_, isolate, ti->argc_, ti->argv_); + delete ti; } @@ -1858,17 +1925,25 @@ static char magic_isolate_cookie_[] = "magic isolate cookie"; static Handle NewIsolate(const Arguments& args) { HandleScope scope; - uv_thread_t* tid = new uv_thread_t; + assert(args[0]->IsArray()); + + Local argv = args[0].As(); + assert(argv->Length() >= 2); + + ThreadInfo* ti = new ThreadInfo(argv); + ti->thread_id_ = NewThreadId(); - if (uv_thread_create(tid, RunIsolate, NULL)) + if (uv_thread_create(&ti->thread_, RunIsolate, ti)) { + delete ti; return Null(); + } Local tpl = ObjectTemplate::New(); tpl->SetInternalFieldCount(2); Local obj = tpl->NewInstance(); obj->SetPointerInInternalField(0, magic_isolate_cookie_); - obj->SetPointerInInternalField(1, tid); + obj->SetPointerInInternalField(1, ti); return scope.Close(obj); } @@ -1883,9 +1958,10 @@ static Handle JoinIsolate(const Arguments& args) { assert(obj->InternalFieldCount() == 2); assert(obj->GetPointerFromInternalField(0) == magic_isolate_cookie_); - uv_thread_t* tid = (uv_thread_t*) obj->GetPointerFromInternalField(1); + ThreadInfo* ti = reinterpret_cast( + obj->GetPointerFromInternalField(1)); - if (uv_thread_join(tid)) + if (uv_thread_join(&ti->thread_)) return False(); // error else return True(); // ok @@ -2038,7 +2114,6 @@ Handle SetupProcessObject(int argc, char *argv[]) { process = Persistent::New(process_template->GetFunction()->NewInstance()); - process->SetAccessor(String::New("title"), ProcessTitleGetter, ProcessTitleSetter); @@ -2634,9 +2709,17 @@ void EmitExit(v8::Handle process_l) { } -void StartThread(Isolate* isolate, int argc, char** argv) { - uv_loop_t* loop = isolate->GetLoop(); +// Create a new isolate with node::Isolate::New() before you call this function +void StartThread(unsigned long thread_id, + Isolate* isolate, + int argc, + char** argv) { + HandleScope scope; + + v8::Isolate::Scope isolate_scope(isolate->GetV8Isolate()); + v8::Context::Scope context_scope(isolate->GetV8Context()); + uv_loop_t* loop = isolate->GetLoop(); uv_prepare_init(loop, &prepare_tick_watcher); uv_prepare_start(&prepare_tick_watcher, PrepareTick); uv_unref(loop); @@ -2688,7 +2771,12 @@ void StartThread(Isolate* isolate, int argc, char** argv) { } Handle process_l = SetupProcessObject(argc, argv); - v8_typed_array::AttachBindings(v8::Context::GetCurrent()->Global()); + + process_l->Set(String::NewSymbol("tid"), + Integer::NewFromUnsigned(thread_id)); + + // FIXME crashes with "CHECK(heap->isolate() == Isolate::Current()) failed" + //v8_typed_array::AttachBindings(v8::Context::GetCurrent()->Global()); // Create all the objects, load modules, do everything. // so your next reading stop should be node::Load()! @@ -2706,24 +2794,21 @@ void StartThread(Isolate* isolate, int argc, char** argv) { int Start(int argc, char *argv[]) { + if (uv_mutex_init(&thread_id_generator_.lock_)) abort(); + // This needs to run *before* V8::Initialize() argv = ProcessInit(argc, argv); v8::V8::Initialize(); v8::HandleScope handle_scope; - // Create the one and only Context. - Persistent context = v8::Context::New(); - v8::Context::Scope context_scope(context); - // Create the main node::Isolate object Isolate* isolate = Isolate::New(uv_default_loop()); - StartThread(isolate, argc, argv); + StartThread(NewThreadId(), isolate, argc, argv); isolate->Dispose(); #ifndef NDEBUG // Clean up. - context.Dispose(); V8::Dispose(); #endif // NDEBUG From 469cb1b7b7fea6a2543547ce4723f2db699ed9d5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 12 Dec 2011 15:47:00 +0100 Subject: [PATCH 13/31] isolates: replace SLIST with ngx_queue_t --- src/node_isolate.cc | 9 +- src/node_isolate.h | 5 +- src/queue.h | 741 -------------------------------------------- 3 files changed, 8 insertions(+), 747 deletions(-) delete mode 100644 src/queue.h diff --git a/src/node_isolate.cc b/src/node_isolate.cc index fcf5db7fd5..7eeaf65ebb 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -35,7 +35,7 @@ Isolate* Isolate::New(uv_loop_t* loop) { Isolate::Isolate(uv_loop_t* loop) { - SLIST_INIT(&at_exit_callbacks_); + ngx_queue_init(&at_exit_callbacks_); loop_ = loop; v8_isolate_ = v8::Isolate::GetCurrent(); @@ -67,19 +67,22 @@ void Isolate::AtExit(AtExitCallback callback, void* arg) { it->callback_ = callback; it->arg_ = arg; - SLIST_INSERT_HEAD(&at_exit_callbacks_, it, entries_); + ngx_queue_insert_head(&at_exit_callbacks_, &it->at_exit_callbacks_); } void Isolate::Dispose() { struct AtExitCallbackInfo* it; + ngx_queue_t* q; NODE_ISOLATE_CHECK(this); - SLIST_FOREACH(it, &at_exit_callbacks_, entries_) { + ngx_queue_foreach(q, &at_exit_callbacks_) { + it = ngx_queue_data(q, struct AtExitCallbackInfo, at_exit_callbacks_); it->callback_(it->arg_); delete it; } + ngx_queue_init(&at_exit_callbacks_); assert(v8_context_->InContext()); v8_context_->Exit(); diff --git a/src/node_isolate.h b/src/node_isolate.h index 74fe7866ba..e5e789d1ba 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -22,7 +22,6 @@ #ifndef SRC_NODE_ISOLATE_H_ #define SRC_NODE_ISOLATE_H_ -#include "queue.h" #include "v8.h" #include "uv.h" #include "node_vars.h" @@ -81,12 +80,12 @@ private: Isolate(uv_loop_t* loop); struct AtExitCallbackInfo { - SLIST_ENTRY(AtExitCallbackInfo) entries_; + ngx_queue_t at_exit_callbacks_; AtExitCallback callback_; void* arg_; }; - SLIST_HEAD(AtExitCallbacks, AtExitCallbackInfo) at_exit_callbacks_; + ngx_queue_t at_exit_callbacks_; v8::Persistent v8_context_; v8::Isolate* v8_isolate_; uv_loop_t* loop_; diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index ea2b7b4b17..0000000000 --- a/src/queue.h +++ /dev/null @@ -1,741 +0,0 @@ -/* $NetBSD: queue.h,v 1.53 2011/11/19 22:51:31 tls Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * A singly-linked list is headed by a single forward pointer. The - * elements are singly linked for minimum space and pointer manipulation - * overhead at the expense of O(n) removal for arbitrary elements. New - * elements can be added to the list after an existing element or at the - * head of the list. Elements being removed from the head of the list - * should use the explicit macro for this purpose for optimum - * efficiency. A singly-linked list may only be traversed in the forward - * direction. Singly-linked lists are ideal for applications with large - * datasets and few or no removals or for implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List functions. - */ -#if defined(_KERNEL) && defined(QUEUEDEBUG) -#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ - if ((head)->lh_first && \ - (head)->lh_first->field.le_prev != &(head)->lh_first) \ - panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); -#define QUEUEDEBUG_LIST_OP(elm, field) \ - if ((elm)->field.le_next && \ - (elm)->field.le_next->field.le_prev != \ - &(elm)->field.le_next) \ - panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ - if (*(elm)->field.le_prev != (elm)) \ - panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ - (elm)->field.le_next = (void *)1L; \ - (elm)->field.le_prev = (void *)1L; -#else -#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) -#define QUEUEDEBUG_LIST_OP(elm, field) -#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) -#endif - -#define LIST_INIT(head) do { \ - (head)->lh_first = NULL; \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - QUEUEDEBUG_LIST_OP((listelm), field) \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - QUEUEDEBUG_LIST_OP((listelm), field) \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (/*CONSTCOND*/0) - -#define LIST_REMOVE(elm, field) do { \ - QUEUEDEBUG_LIST_OP((elm), field) \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ -} while (/*CONSTCOND*/0) - -#define LIST_FOREACH(var, head, field) \ - for ((var) = ((head)->lh_first); \ - (var); \ - (var) = ((var)->field.le_next)) - -#define LIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = LIST_FIRST((head)); \ - (var) && ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) -/* - * List access methods. - */ -#define LIST_EMPTY(head) ((head)->lh_first == NULL) -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) do { \ - (head)->slh_first = NULL; \ -} while (/*CONSTCOND*/0) - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = (head)->slh_first; \ - while(curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE_AFTER(slistelm, field) do { \ - (slistelm)->field.sle_next = \ - SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ -} while (/*CONSTCOND*/0) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) - -#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = SLIST_FIRST((head)); \ - (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -/* - * Singly-linked List access methods. - */ -#define SLIST_EMPTY(head) ((head)->slh_first == NULL) -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - - -/* - * Singly-linked Tail queue declarations. - */ -#define STAILQ_HEAD(name, type) \ -struct name { \ - struct type *stqh_first; /* first element */ \ - struct type **stqh_last; /* addr of last next element */ \ -} - -#define STAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).stqh_first } - -#define STAILQ_ENTRY(type) \ -struct { \ - struct type *stqe_next; /* next element */ \ -} - -/* - * Singly-linked Tail queue functions. - */ -#define STAILQ_INIT(head) do { \ - (head)->stqh_first = NULL; \ - (head)->stqh_last = &(head)->stqh_first; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ - (head)->stqh_last = &(elm)->field.stqe_next; \ - (head)->stqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.stqe_next = NULL; \ - *(head)->stqh_last = (elm); \ - (head)->stqh_last = &(elm)->field.stqe_next; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ - (head)->stqh_last = &(elm)->field.stqe_next; \ - (listelm)->field.stqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define STAILQ_REMOVE_HEAD(head, field) do { \ - if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ - (head)->stqh_last = &(head)->stqh_first; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_REMOVE(head, elm, type, field) do { \ - if ((head)->stqh_first == (elm)) { \ - STAILQ_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->stqh_first; \ - while (curelm->field.stqe_next != (elm)) \ - curelm = curelm->field.stqe_next; \ - if ((curelm->field.stqe_next = \ - curelm->field.stqe_next->field.stqe_next) == NULL) \ - (head)->stqh_last = &(curelm)->field.stqe_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define STAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->stqh_first); \ - (var); \ - (var) = ((var)->field.stqe_next)) - -#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = STAILQ_FIRST((head)); \ - (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define STAILQ_CONCAT(head1, head2) do { \ - if (!STAILQ_EMPTY((head2))) { \ - *(head1)->stqh_last = (head2)->stqh_first; \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -#define STAILQ_LAST(head, type, field) \ - (STAILQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->stqh_last) - offsetof(struct type, field)))) - -/* - * Singly-linked Tail queue access methods. - */ -#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) -#define STAILQ_FIRST(head) ((head)->stqh_first) -#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) - - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ - if ((head)->sqh_first == (elm)) { \ - SIMPLEQ_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->sqh_first; \ - while (curelm->field.sqe_next != (elm)) \ - curelm = curelm->field.sqe_next; \ - if ((curelm->field.sqe_next = \ - curelm->field.sqe_next->field.sqe_next) == NULL) \ - (head)->sqh_last = &(curelm)->field.sqe_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for ((var) = ((head)->sqh_first); \ - (var); \ - (var) = ((var)->field.sqe_next)) - -#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ - for ((var) = ((head)->sqh_first); \ - (var) && ((next = ((var)->field.sqe_next)), 1); \ - (var) = (next)) - -#define SIMPLEQ_CONCAT(head1, head2) do { \ - if (!SIMPLEQ_EMPTY((head2))) { \ - *(head1)->sqh_last = (head2)->sqh_first; \ - (head1)->sqh_last = (head2)->sqh_last; \ - SIMPLEQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_LAST(head, type, field) \ - (SIMPLEQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->sqh_last) - offsetof(struct type, field)))) - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - - -/* - * Tail queue definitions. - */ -#define _TAILQ_HEAD(name, type, qual) \ -struct name { \ - qual type *tqh_first; /* first element */ \ - qual type *qual *tqh_last; /* addr of last next element */ \ -} -#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define _TAILQ_ENTRY(type, qual) \ -struct { \ - qual type *tqe_next; /* next element */ \ - qual type *qual *tqe_prev; /* address of previous next element */\ -} -#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) - -/* - * Tail queue functions. - */ -#if defined(_KERNEL) && defined(QUEUEDEBUG) -#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ - if ((head)->tqh_first && \ - (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ - panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ - if (*(head)->tqh_last != NULL) \ - panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_OP(elm, field) \ - if ((elm)->field.tqe_next && \ - (elm)->field.tqe_next->field.tqe_prev != \ - &(elm)->field.tqe_next) \ - panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ - if (*(elm)->field.tqe_prev != (elm)) \ - panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ - if ((elm)->field.tqe_next == NULL && \ - (head)->tqh_last != &(elm)->field.tqe_next) \ - panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \ - (head), (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ - (elm)->field.tqe_next = (void *)1L; \ - (elm)->field.tqe_prev = (void *)1L; -#else -#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) -#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) -#define QUEUEDEBUG_TAILQ_OP(elm, field) -#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) -#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) -#endif - -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QUEUEDEBUG_TAILQ_OP((listelm), field) \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - QUEUEDEBUG_TAILQ_OP((listelm), field) \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ - QUEUEDEBUG_TAILQ_OP((elm), field) \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ -} while (/*CONSTCOND*/0) - -#define TAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->tqh_first); \ - (var); \ - (var) = ((var)->field.tqe_next)) - -#define TAILQ_FOREACH_SAFE(var, head, field, next) \ - for ((var) = ((head)->tqh_first); \ - (var) != NULL && ((next) = TAILQ_NEXT(var, field), 1); \ - (var) = (next)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ - (var); \ - (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) - -#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var) && ((prev) = TAILQ_PREV((var), headname, field), 1);\ - (var) = (prev)) - -#define TAILQ_CONCAT(head1, head2, field) do { \ - if (!TAILQ_EMPTY(head2)) { \ - *(head1)->tqh_last = (head2)->tqh_first; \ - (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ - (head1)->tqh_last = (head2)->tqh_last; \ - TAILQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -/* - * Tail queue access methods. - */ -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - - -/* - * Circular queue definitions. - */ -#if defined(_KERNEL) && defined(QUEUEDEBUG) -#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ - if ((head)->cqh_first != (void *)(head) && \ - (head)->cqh_first->field.cqe_prev != (void *)(head)) \ - panic("CIRCLEQ head forw %p %s:%d", (head), \ - __FILE__, __LINE__); \ - if ((head)->cqh_last != (void *)(head) && \ - (head)->cqh_last->field.cqe_next != (void *)(head)) \ - panic("CIRCLEQ head back %p %s:%d", (head), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ - if ((elm)->field.cqe_next == (void *)(head)) { \ - if ((head)->cqh_last != (elm)) \ - panic("CIRCLEQ elm last %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - } else { \ - if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ - panic("CIRCLEQ elm forw %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - } \ - if ((elm)->field.cqe_prev == (void *)(head)) { \ - if ((head)->cqh_first != (elm)) \ - panic("CIRCLEQ elm first %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - } else { \ - if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ - panic("CIRCLEQ elm prev %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - } -#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ - (elm)->field.cqe_next = (void *)1L; \ - (elm)->field.cqe_prev = (void *)1L; -#else -#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) -#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) -#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) -#endif - -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { (void *)&head, (void *)&head } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = (void *)(head); \ - (head)->cqh_last = (void *)(head); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == (void *)(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == (void *)(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = (void *)(head); \ - if ((head)->cqh_last == (void *)(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - (elm)->field.cqe_next = (void *)(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == (void *)(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ - if ((elm)->field.cqe_next == (void *)(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == (void *)(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for ((var) = ((head)->cqh_first); \ - (var) != (const void *)(head); \ - (var) = ((var)->field.cqe_next)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for ((var) = ((head)->cqh_last); \ - (var) != (const void *)(head); \ - (var) = ((var)->field.cqe_prev)) - -/* - * Circular queue access methods. - */ -#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) - -#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ - (((elm)->field.cqe_next == (void *)(head)) \ - ? ((head)->cqh_first) \ - : (elm->field.cqe_next)) -#define CIRCLEQ_LOOP_PREV(head, elm, field) \ - (((elm)->field.cqe_prev == (void *)(head)) \ - ? ((head)->cqh_last) \ - : (elm->field.cqe_prev)) - -#endif /* !_SYS_QUEUE_H_ */ From 39a492799dca305f330c4693765ea8a951fc0d19 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 13 Dec 2011 11:49:32 -0800 Subject: [PATCH 14/31] move thread_id to node_isolate.cc --- src/node.cc | 37 +++++++------------------------------ src/node_isolate.cc | 17 +++++++++++++++++ src/node_isolate.h | 5 +++++ 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/node.cc b/src/node.cc index 307ecf5df8..194a6de987 100644 --- a/src/node.cc +++ b/src/node.cc @@ -140,10 +140,8 @@ static const char* eval_string; static bool print_eval; static void CheckStatus(uv_timer_t* watcher, int status); -static unsigned long NewThreadId(); -void StartThread(unsigned long thread_id, - Isolate* isolate, +void StartThread(Isolate* isolate, int argc, char** argv); @@ -1851,25 +1849,7 @@ static Handle Binding(const Arguments& args) { } -static struct { - uv_mutex_t lock_; - unsigned long counter_; -} thread_id_generator_; - - -static unsigned long NewThreadId() { - unsigned long thread_id; - - uv_mutex_lock(&thread_id_generator_.lock_); - thread_id = ++thread_id_generator_.counter_; - uv_mutex_unlock(&thread_id_generator_.lock_); - - return thread_id; -} - - struct ThreadInfo { - unsigned long thread_id_; uv_thread_t thread_; char** argv_; int argc_; @@ -1914,7 +1894,7 @@ static void RunIsolate(void* arg) { uv_loop_t* loop = uv_loop_new(); Isolate* isolate = Isolate::New(loop); - StartThread(ti->thread_id_, isolate, ti->argc_, ti->argv_); + StartThread(isolate, ti->argc_, ti->argv_); delete ti; } @@ -1931,7 +1911,6 @@ static Handle NewIsolate(const Arguments& args) { assert(argv->Length() >= 2); ThreadInfo* ti = new ThreadInfo(argv); - ti->thread_id_ = NewThreadId(); if (uv_thread_create(&ti->thread_, RunIsolate, ti)) { delete ti; @@ -2710,8 +2689,7 @@ void EmitExit(v8::Handle process_l) { // Create a new isolate with node::Isolate::New() before you call this function -void StartThread(unsigned long thread_id, - Isolate* isolate, +void StartThread(node::Isolate* isolate, int argc, char** argv) { HandleScope scope; @@ -2773,7 +2751,7 @@ void StartThread(unsigned long thread_id, Handle process_l = SetupProcessObject(argc, argv); process_l->Set(String::NewSymbol("tid"), - Integer::NewFromUnsigned(thread_id)); + Integer::NewFromUnsigned(isolate->id_)); // FIXME crashes with "CHECK(heap->isolate() == Isolate::Current()) failed" //v8_typed_array::AttachBindings(v8::Context::GetCurrent()->Global()); @@ -2794,8 +2772,6 @@ void StartThread(unsigned long thread_id, int Start(int argc, char *argv[]) { - if (uv_mutex_init(&thread_id_generator_.lock_)) abort(); - // This needs to run *before* V8::Initialize() argv = ProcessInit(argc, argv); @@ -2803,8 +2779,9 @@ int Start(int argc, char *argv[]) { v8::HandleScope handle_scope; // Create the main node::Isolate object - Isolate* isolate = Isolate::New(uv_default_loop()); - StartThread(NewThreadId(), isolate, argc, argv); + node::Isolate::Initialize(); + Isolate* isolate = node::Isolate::New(uv_default_loop()); + StartThread(isolate, argc, argv); isolate->Dispose(); #ifndef NDEBUG diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 7eeaf65ebb..adaa4b254e 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -26,8 +26,19 @@ #include + namespace node { +static volatile bool initialized; +static volatile int id; +static uv_mutex_t id_lock; + +void Isolate::Initialize() { + if (!initialized) { + initialized = true; + if (uv_mutex_init(&id_lock)) abort(); + } +} Isolate* Isolate::New(uv_loop_t* loop) { return new Isolate(loop); @@ -35,6 +46,12 @@ Isolate* Isolate::New(uv_loop_t* loop) { Isolate::Isolate(uv_loop_t* loop) { + assert(initialized && "node::Isolate::Initialize() hasn't been called"); + + uv_mutex_lock(&id_lock); + id_ = ++id; + uv_mutex_unlock(&id_lock); + ngx_queue_init(&at_exit_callbacks_); loop_ = loop; diff --git a/src/node_isolate.h b/src/node_isolate.h index e5e789d1ba..5529da2387 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -43,6 +43,9 @@ namespace node { class Isolate { public: + // Call this before instantiating any Isolate + static void Initialize(); + typedef void (*AtExitCallback)(void* arg); static Isolate* New(uv_loop_t* loop); @@ -76,6 +79,8 @@ public: struct globals* Globals(); + unsigned int id_; + private: Isolate(uv_loop_t* loop); From 78a25696f1ef88e245c9b88a25244d73e517ecd1 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 13 Dec 2011 14:48:36 -0800 Subject: [PATCH 15/31] Move uv loop initialization into isolate --- src/node.cc | 5 ++--- src/node_isolate.cc | 14 ++++++++++---- src/node_isolate.h | 4 ++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/node.cc b/src/node.cc index 194a6de987..86b9121223 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1891,8 +1891,7 @@ struct ThreadInfo { static void RunIsolate(void* arg) { ThreadInfo* ti = reinterpret_cast(arg); - uv_loop_t* loop = uv_loop_new(); - Isolate* isolate = Isolate::New(loop); + Isolate* isolate = Isolate::New(); StartThread(isolate, ti->argc_, ti->argv_); delete ti; @@ -2780,7 +2779,7 @@ int Start(int argc, char *argv[]) { // Create the main node::Isolate object node::Isolate::Initialize(); - Isolate* isolate = node::Isolate::New(uv_default_loop()); + Isolate* isolate = node::Isolate::New(); StartThread(isolate, argc, argv); isolate->Dispose(); diff --git a/src/node_isolate.cc b/src/node_isolate.cc index adaa4b254e..138e29e8eb 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -40,20 +40,26 @@ void Isolate::Initialize() { } } -Isolate* Isolate::New(uv_loop_t* loop) { - return new Isolate(loop); + +Isolate* Isolate::New() { + return new Isolate(); } -Isolate::Isolate(uv_loop_t* loop) { +Isolate::Isolate() { assert(initialized && "node::Isolate::Initialize() hasn't been called"); uv_mutex_lock(&id_lock); id_ = ++id; uv_mutex_unlock(&id_lock); + if (id_ == 1) { + loop_ = uv_default_loop(); + } else { + loop_ = uv_loop_new(); + } + ngx_queue_init(&at_exit_callbacks_); - loop_ = loop; v8_isolate_ = v8::Isolate::GetCurrent(); if (v8_isolate_ == NULL) { diff --git a/src/node_isolate.h b/src/node_isolate.h index 5529da2387..8557ffa776 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -48,7 +48,7 @@ public: typedef void (*AtExitCallback)(void* arg); - static Isolate* New(uv_loop_t* loop); + static Isolate* New(); static Isolate* GetCurrent() { return reinterpret_cast(v8::Isolate::GetCurrent()->GetData()); @@ -82,7 +82,7 @@ public: unsigned int id_; private: - Isolate(uv_loop_t* loop); + Isolate(); struct AtExitCallbackInfo { ngx_queue_t at_exit_callbacks_; From 1a433b9637791d57c4ef2c94675e9a6ae1722b81 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 15 Dec 2011 14:20:27 -0800 Subject: [PATCH 16/31] Add link-list of all isolates --- src/node.cc | 10 ++++++++++ src/node_isolate.cc | 35 +++++++++++++++++++++++++++++++---- src/node_isolate.h | 4 ++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/node.cc b/src/node.cc index 86b9121223..b5331f7a75 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1894,7 +1894,10 @@ static void RunIsolate(void* arg) { Isolate* isolate = Isolate::New(); StartThread(isolate, ti->argc_, ti->argv_); + isolate->Dispose(); + delete ti; + delete isolate; } @@ -1927,6 +1930,12 @@ static Handle NewIsolate(const Arguments& args) { } +static Handle CountIsolate(const Arguments& args) { + HandleScope scope; + return scope.Close(Integer::New(Isolate::Count())); +} + + static Handle JoinIsolate(const Arguments& args) { HandleScope scope; @@ -2218,6 +2227,7 @@ Handle SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "binding", Binding); NODE_SET_METHOD(process, "_newIsolate", NewIsolate); + NODE_SET_METHOD(process, "_countIsolate", CountIsolate); NODE_SET_METHOD(process, "_joinIsolate", JoinIsolate); return process; diff --git a/src/node_isolate.cc b/src/node_isolate.cc index 138e29e8eb..ecc330e189 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -29,14 +29,19 @@ namespace node { + static volatile bool initialized; static volatile int id; -static uv_mutex_t id_lock; +static volatile int isolate_count; +static uv_mutex_t list_lock; +static ngx_queue_t list_head; + void Isolate::Initialize() { if (!initialized) { initialized = true; - if (uv_mutex_init(&id_lock)) abort(); + if (uv_mutex_init(&list_lock)) abort(); + ngx_queue_init(&list_head); } } @@ -46,12 +51,17 @@ Isolate* Isolate::New() { } +int Isolate::Count() { + return isolate_count; +} + + Isolate::Isolate() { + uv_mutex_lock(&list_lock); + assert(initialized && "node::Isolate::Initialize() hasn't been called"); - uv_mutex_lock(&id_lock); id_ = ++id; - uv_mutex_unlock(&id_lock); if (id_ == 1) { loop_ = uv_default_loop(); @@ -61,6 +71,14 @@ Isolate::Isolate() { ngx_queue_init(&at_exit_callbacks_); + ngx_queue_init(&list_member_); + + // Add this isolate into the list of all isolates. + ngx_queue_insert_tail(&list_head, &list_member_); + isolate_count++; + + uv_mutex_unlock(&list_lock); + v8_isolate_ = v8::Isolate::GetCurrent(); if (v8_isolate_ == NULL) { v8_isolate_ = v8::Isolate::New(); @@ -95,6 +113,8 @@ void Isolate::AtExit(AtExitCallback callback, void* arg) { void Isolate::Dispose() { + uv_mutex_lock(&list_lock); + struct AtExitCallbackInfo* it; ngx_queue_t* q; @@ -115,6 +135,13 @@ void Isolate::Dispose() { v8_isolate_->Exit(); v8_isolate_->Dispose(); v8_isolate_ = NULL; + + ngx_queue_remove(&list_member_); + isolate_count--; + assert(isolate_count >= 0); + assert(isolate_count > 0 || ngx_queue_empty(&list_head)); + + uv_mutex_unlock(&list_lock); } diff --git a/src/node_isolate.h b/src/node_isolate.h index 8557ffa776..b4683b2636 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -45,6 +45,7 @@ class Isolate { public: // Call this before instantiating any Isolate static void Initialize(); + static int Count(); typedef void (*AtExitCallback)(void* arg); @@ -95,6 +96,9 @@ private: v8::Isolate* v8_isolate_; uv_loop_t* loop_; + // Each isolate is a member of the static list_head. + ngx_queue_t list_member_; + // Global variables for this isolate. struct globals globals_; }; From 2d8c1fe1ed7ed3be91cd2cb12eda626e77a61c1e Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 16 Dec 2011 23:23:34 -0800 Subject: [PATCH 17/31] Move prog_start_time init after isolate assigned --- src/node.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node.cc b/src/node.cc index b5331f7a75..a2907391d6 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2632,9 +2632,6 @@ static Handle DebugPause(const Arguments& args) { char** ProcessInit(int argc, char *argv[]) { - // Initialize prog_start_time to get relative uptime. - uv_uptime(&prog_start_time); - // Hack aroung with the argv pointer. Used for process.title = "blah". argv = uv_setup_args(argc, argv); @@ -2765,6 +2762,9 @@ void StartThread(node::Isolate* isolate, // FIXME crashes with "CHECK(heap->isolate() == Isolate::Current()) failed" //v8_typed_array::AttachBindings(v8::Context::GetCurrent()->Global()); + // Initialize prog_start_time to get relative uptime. + uv_uptime(&prog_start_time); + // Create all the objects, load modules, do everything. // so your next reading stop should be node::Load()! Load(process_l); From 41062e71a7fb5f6447d521f9cd9e6a34a2ea4bcc Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 18 Dec 2011 10:56:48 -0800 Subject: [PATCH 18/31] Join all threads at end of main thread Require reorganizing the isolates somewhat. Add a very simple test. --- src/node.cc | 88 ++++++++++++++---------------------- src/node_isolate.cc | 65 +++++++++++++++++--------- src/node_isolate.h | 25 ++++++++-- test/simple/test-isolates.js | 11 +++++ 4 files changed, 107 insertions(+), 82 deletions(-) create mode 100644 test/simple/test-isolates.js diff --git a/src/node.cc b/src/node.cc index a2907391d6..952ed492cb 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1849,54 +1849,11 @@ static Handle Binding(const Arguments& args) { } -struct ThreadInfo { - uv_thread_t thread_; - char** argv_; - int argc_; - - ThreadInfo(int argc, char** argv) { - argc_ = argc; - argv_ = new char*[argc_ + 1]; - - for (int i = 0; i < argc_; ++i) { - size_t size = 1 + strlen(argv[i]); - argv_[i] = new char[size]; - memcpy(argv_[i], argv[i], size); - } - argv_[argc_] = NULL; - } - - ThreadInfo(Handle args) { - argc_ = args->Length(); - argv_ = new char*[argc_ + 1]; - - for (int i = 0; i < argc_; ++i) { - String::Utf8Value str(args->Get(i)); - size_t size = 1 + strlen(*str); - argv_[i] = new char[size]; - memcpy(argv_[i], *str, size); - } - argv_[argc_] = NULL; - } - - ~ThreadInfo() { - for (int i = 0; i < argc_; ++i) { - delete[] argv_[i]; - } - delete argv_; - } -}; - - static void RunIsolate(void* arg) { - ThreadInfo* ti = reinterpret_cast(arg); - - Isolate* isolate = Isolate::New(); - - StartThread(isolate, ti->argc_, ti->argv_); + node::Isolate* isolate = reinterpret_cast(arg); + isolate->Enter(); + StartThread(isolate, isolate->argc_, isolate->argv_); isolate->Dispose(); - - delete ti; delete isolate; } @@ -1912,10 +1869,23 @@ static Handle NewIsolate(const Arguments& args) { Local argv = args[0].As(); assert(argv->Length() >= 2); - ThreadInfo* ti = new ThreadInfo(argv); + // 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(); - if (uv_thread_create(&ti->thread_, RunIsolate, ti)) { - delete ti; + // 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 (uv_thread_create(&isolate->tid_, RunIsolate, isolate)) { + delete isolate; return Null(); } @@ -1924,7 +1894,7 @@ static Handle NewIsolate(const Arguments& args) { Local obj = tpl->NewInstance(); obj->SetPointerInInternalField(0, magic_isolate_cookie_); - obj->SetPointerInInternalField(1, ti); + obj->SetPointerInInternalField(1, isolate); return scope.Close(obj); } @@ -1945,10 +1915,10 @@ static Handle JoinIsolate(const Arguments& args) { assert(obj->InternalFieldCount() == 2); assert(obj->GetPointerFromInternalField(0) == magic_isolate_cookie_); - ThreadInfo* ti = reinterpret_cast( + Isolate* ti = reinterpret_cast( obj->GetPointerFromInternalField(1)); - if (uv_thread_join(&ti->thread_)) + if (uv_thread_join(&ti->tid_)) return False(); // error else return True(); // ok @@ -2700,8 +2670,7 @@ void StartThread(node::Isolate* isolate, char** argv) { HandleScope scope; - v8::Isolate::Scope isolate_scope(isolate->GetV8Isolate()); - v8::Context::Scope context_scope(isolate->GetV8Context()); + assert(node::Isolate::GetCurrent() == isolate); uv_loop_t* loop = isolate->GetLoop(); uv_prepare_init(loop, &prepare_tick_watcher); @@ -2787,12 +2756,21 @@ int Start(int argc, char *argv[]) { v8::V8::Initialize(); v8::HandleScope handle_scope; + // Get the id of the this, the main, thread. + uv_thread_t tid = uv_thread_self(); + // Create the main node::Isolate object node::Isolate::Initialize(); - Isolate* isolate = node::Isolate::New(); + Isolate* isolate = new node::Isolate(); + isolate->tid_ = tid; + isolate->Enter(); StartThread(isolate, argc, argv); isolate->Dispose(); + // The main thread/isolate is done. Wait for all other thread/isolates to + // finish. + node::Isolate::JoinAll(); + #ifndef NDEBUG // Clean up. V8::Dispose(); diff --git a/src/node_isolate.cc b/src/node_isolate.cc index ecc330e189..e2d39222b9 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -46,13 +46,31 @@ void Isolate::Initialize() { } -Isolate* Isolate::New() { - return new Isolate(); +int Isolate::Count() { + return isolate_count; } -int Isolate::Count() { - return isolate_count; +void Isolate::JoinAll() { + uv_mutex_lock(&list_lock); + + while (ngx_queue_empty(&list_head) == false) { + ngx_queue_t* q = ngx_queue_head(&list_head); + assert(q); + Isolate* isolate = ngx_queue_data(q, Isolate, list_member_); + assert(isolate); + + // Unlock the list while we join the thread. + uv_mutex_unlock(&list_lock); + + uv_thread_join(&isolate->tid_); + + // Relock to check the next element in the list. + uv_mutex_lock(&list_lock); + } + + // Unlock the list finally. + uv_mutex_unlock(&list_lock); } @@ -79,19 +97,11 @@ Isolate::Isolate() { uv_mutex_unlock(&list_lock); - v8_isolate_ = v8::Isolate::GetCurrent(); - if (v8_isolate_ == NULL) { - v8_isolate_ = v8::Isolate::New(); - v8_isolate_->Enter(); - } - + v8_isolate_ = v8::Isolate::New(); assert(v8_isolate_->GetData() == NULL); v8_isolate_->SetData(this); - v8_context_ = v8::Context::New(); - v8_context_->Enter(); - - globals_init(&globals_); + globals_init_ = false; } @@ -112,20 +122,31 @@ void Isolate::AtExit(AtExitCallback callback, void* arg) { } +void Isolate::Enter() { + v8_isolate_->Enter(); + + if (v8_context_.IsEmpty()) { + v8_context_ = v8::Context::New(); + } + v8_context_->Enter(); + + if (!globals_init_) { + globals_init_ = true; + globals_init(&globals_); + } + + NODE_ISOLATE_CHECK(this); +} + + void Isolate::Dispose() { uv_mutex_lock(&list_lock); + NODE_ISOLATE_CHECK(this); + struct AtExitCallbackInfo* it; ngx_queue_t* q; - NODE_ISOLATE_CHECK(this); - - ngx_queue_foreach(q, &at_exit_callbacks_) { - it = ngx_queue_data(q, struct AtExitCallbackInfo, at_exit_callbacks_); - it->callback_(it->arg_); - delete it; - } - ngx_queue_init(&at_exit_callbacks_); assert(v8_context_->InContext()); v8_context_->Exit(); diff --git a/src/node_isolate.h b/src/node_isolate.h index b4683b2636..e2dac08880 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -43,13 +43,17 @@ namespace node { class Isolate { public: + char** argv_; + int argc_; + uv_thread_t tid_; + // Call this before instantiating any Isolate static void Initialize(); static int Count(); typedef void (*AtExitCallback)(void* arg); - static Isolate* New(); + static void JoinAll(); static Isolate* GetCurrent() { return reinterpret_cast(v8::Isolate::GetCurrent()->GetData()); @@ -75,16 +79,26 @@ public: */ void AtExit(AtExitCallback callback, void *arg); - /* Shutdown the isolate. Call this method at thread death. */ - void Dispose(); - struct globals* Globals(); unsigned int id_; -private: + // This constructor is used for every non-main thread Isolate(); + ~Isolate() { + if (argv_) { + delete argv_; + } + } + + void Enter(); + + /* Shutdown the isolate. Call this method at thread death. */ + void Dispose(); + +private: + struct AtExitCallbackInfo { ngx_queue_t at_exit_callbacks_; AtExitCallback callback_; @@ -101,6 +115,7 @@ private: // Global variables for this isolate. struct globals globals_; + bool globals_init_; }; } // namespace node diff --git a/test/simple/test-isolates.js b/test/simple/test-isolates.js new file mode 100644 index 0000000000..be09ffc7a8 --- /dev/null +++ b/test/simple/test-isolates.js @@ -0,0 +1,11 @@ +console.log("count: %d", process._countIsolate()); + +if (process.tid === 1) { + var isolate = process._newIsolate(process.argv); + //process._joinIsolate(isolate); + console.error("master"); + console.log("count: %d", process._countIsolate()); +} else { + console.error("FUCK YEAH!"); + console.log("count: %d", process._countIsolate()); +} From a8506c41c6ecf6961c7502c97dcc1032440f14a4 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 18 Dec 2011 22:49:20 -0800 Subject: [PATCH 19/31] node_file.cc should use NODE_LOOP() --- src/node_file.cc | 6 +++--- test/simple/test-isolates.js | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 4913b699c3..718b0557e9 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -225,7 +225,7 @@ struct fs_req_wrap { #define ASYNC_CALL(func, callback, ...) \ FSReqWrap* req_wrap = new FSReqWrap(); \ - int r = uv_fs_##func(uv_default_loop(), &req_wrap->req_, \ + int r = uv_fs_##func(NODE_LOOP(), &req_wrap->req_, \ __VA_ARGS__, After); \ assert(r == 0); \ req_wrap->object_->Set(oncomplete_sym, callback); \ @@ -234,9 +234,9 @@ struct fs_req_wrap { #define SYNC_CALL(func, path, ...) \ fs_req_wrap req_wrap; \ - int result = uv_fs_##func(uv_default_loop(), &req_wrap.req, __VA_ARGS__, NULL); \ + int result = uv_fs_##func(NODE_LOOP(), &req_wrap.req, __VA_ARGS__, NULL); \ if (result < 0) { \ - int code = uv_last_error(uv_default_loop()).code; \ + int code = uv_last_error(NODE_LOOP()).code; \ return ThrowException(UVException(code, #func, "", path)); \ } diff --git a/test/simple/test-isolates.js b/test/simple/test-isolates.js index be09ffc7a8..15b643ebe1 100644 --- a/test/simple/test-isolates.js +++ b/test/simple/test-isolates.js @@ -1,11 +1,27 @@ +var fs = require('fs'); + console.log("count: %d", process._countIsolate()); if (process.tid === 1) { var isolate = process._newIsolate(process.argv); //process._joinIsolate(isolate); console.error("master"); - console.log("count: %d", process._countIsolate()); + fs.stat(__dirname, function(err, stat) { + if (err) { + console.error("thread 1 error!"); + throw err; + } + console.error('thread 1', stat); + }); + console.log("thread 1 count: %d", process._countIsolate()); } else { - console.error("FUCK YEAH!"); - console.log("count: %d", process._countIsolate()); + console.error("slave"); + fs.stat(__dirname, function(err, stat) { + if (err) { + console.error("thread 2 error!"); + throw err; + } + console.error('thread 2', stat); + }); + console.error("thread 2 count: %d", process._countIsolate()); } From 8c476371f5996a261d17a6d4af6c8d9bb127eff9 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 18 Dec 2011 23:39:49 -0800 Subject: [PATCH 20/31] Change isolate test to demo EIO bug --- test/simple/test-isolates.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/test/simple/test-isolates.js b/test/simple/test-isolates.js index 15b643ebe1..810ae95b8d 100644 --- a/test/simple/test-isolates.js +++ b/test/simple/test-isolates.js @@ -1,4 +1,5 @@ var fs = require('fs'); +var http = require('http'); console.log("count: %d", process._countIsolate()); @@ -7,21 +8,31 @@ if (process.tid === 1) { //process._joinIsolate(isolate); console.error("master"); fs.stat(__dirname, function(err, stat) { - if (err) { - console.error("thread 1 error!"); - throw err; - } - console.error('thread 1', 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", process._countIsolate()); } else { console.error("slave"); fs.stat(__dirname, function(err, stat) { - if (err) { - console.error("thread 2 error!"); - throw err; - } - console.error('thread 2', 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); + }); + }, 500); + console.error("thread 2 count: %d", process._countIsolate()); } From 87bb848268820ec9b91ea860dadf3ba3769cdea4 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 21 Dec 2011 14:02:52 -0800 Subject: [PATCH 21/31] Remove node_isolate.h from node.h --- src/cares_wrap.cc | 1 + src/fs_event_wrap.cc | 1 + src/handle_wrap.cc | 1 + src/node.cc | 1 + src/node.h | 1 - src/node_crypto.cc | 1 + src/node_file.cc | 1 + src/node_zlib.cc | 1 + src/pipe_wrap.cc | 1 + src/process_wrap.cc | 1 + src/stream_wrap.cc | 1 + src/tcp_wrap.cc | 1 + src/timer_wrap.cc | 1 + src/tty_wrap.cc | 1 + src/udp_wrap.cc | 1 + 15 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 976f774c9d..b3e4bd50b2 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index ca584a7938..2e6dee4c47 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index bce82bdb05..2aff643eb3 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include namespace node { diff --git a/src/node.cc b/src/node.cc index 952ed492cb..61b4547c34 100644 --- a/src/node.cc +++ b/src/node.cc @@ -20,6 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include #include diff --git a/src/node.h b/src/node.h index e0df48e88f..e8c532464e 100644 --- a/src/node.h +++ b/src/node.h @@ -64,7 +64,6 @@ #include #include -#include #include #if NODE_WANT_INTERNALS diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 70ae99d1fb..9cb7280771 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/src/node_file.cc b/src/node_file.cc index 718b0557e9..e7293fb00e 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -22,6 +22,7 @@ #include "node.h" #include "node_file.h" #include "node_buffer.h" +#include #ifdef __POSIX__ # include "node_stat_watcher.h" #endif diff --git a/src/node_zlib.cc b/src/node_zlib.cc index dc0bc4691c..eb7d06d9c4 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -29,6 +29,7 @@ #include #include +#include #include #include diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 9f66751150..483118ad84 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 50c88a9db1..3d988ac1f5 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 2e14328472..fbe9152b5a 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 2197850d16..83fdcdd14a 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index fb86036923..82b7ec984e 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #define UNWRAP \ assert(!args.Holder().IsEmpty()); \ diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index b3ed49fa84..1ec8410f16 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 81405f4f69..f0c487eced 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include From 02f24f54ba4cbe6f4d8e6eecd6eb08c07a4569bd Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 21 Dec 2011 13:55:05 -0800 Subject: [PATCH 22/31] Add shared-buffer isolate addon test --- test/addons/shared-buffer/binding.cc | 55 +++++++++++++++++++++++++++ test/addons/shared-buffer/binding.gyp | 8 ++++ test/addons/shared-buffer/test.js | 18 +++++++++ 3 files changed, 81 insertions(+) create mode 100644 test/addons/shared-buffer/binding.cc create mode 100644 test/addons/shared-buffer/binding.gyp create mode 100644 test/addons/shared-buffer/test.js diff --git a/test/addons/shared-buffer/binding.cc b/test/addons/shared-buffer/binding.cc new file mode 100644 index 0000000000..d81f1d4f22 --- /dev/null +++ b/test/addons/shared-buffer/binding.cc @@ -0,0 +1,55 @@ +#include +#include +#include + +using namespace v8; + +extern "C" { + void init(Handle target); +} + + +#define BUFSIZE 1024 +static uint8_t buf[BUFSIZE]; +static uv_mutex_t lock; + + +Handle 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 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 val = Integer::New(buf[index]); + + uv_mutex_unlock(&lock); + + return scope.Close(val); +} + + +void init(Handle 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); +} diff --git a/test/addons/shared-buffer/binding.gyp b/test/addons/shared-buffer/binding.gyp new file mode 100644 index 0000000000..3bfb84493f --- /dev/null +++ b/test/addons/shared-buffer/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/test/addons/shared-buffer/test.js b/test/addons/shared-buffer/test.js new file mode 100644 index 0000000000..ab7da43bd6 --- /dev/null +++ b/test/addons/shared-buffer/test.js @@ -0,0 +1,18 @@ +var assert = require('assert'); +var binding = require('./out/Release/binding'); + +console.log("binding.length =", binding.length); + +if (process.tid === 1) { + var isolate = process._newIsolate(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)); + } +} + From c481f97a9c16c812979f8408171a5cece4b1d918 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 22 Dec 2011 17:09:00 +0100 Subject: [PATCH 23/31] uv: upgrade to d6a06b8 --- deps/uv/AUTHORS | 1 + deps/uv/include/uv-private/eio.h | 119 +++++++++++++--------- deps/uv/include/uv.h | 5 +- deps/uv/src/unix/core.c | 3 +- deps/uv/src/unix/dl.c | 22 +--- deps/uv/src/unix/eio/eio.c | 170 +++++++++++++++---------------- deps/uv/src/unix/fs.c | 20 ++-- deps/uv/src/unix/uv-eio.c | 26 ++--- deps/uv/test/test-list.h | 2 - 9 files changed, 182 insertions(+), 186 deletions(-) diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index dd40b7af19..f1fef1ca2e 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -39,3 +39,4 @@ Bruce Mitchener Maciej Małecki Yasuhiro Matsumoto Daisuke Murase +Paddy Byers diff --git a/deps/uv/include/uv-private/eio.h b/deps/uv/include/uv-private/eio.h index 450df6ba29..aab9988b81 100644 --- a/deps/uv/include/uv-private/eio.h +++ b/deps/uv/include/uv-private/eio.h @@ -206,6 +206,28 @@ enum { EIO_PRI_DEFAULT = 0 }; +#define ETP_PRI_MIN EIO_PRI_MIN +#define ETP_PRI_MAX EIO_PRI_MAX + +#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) + +#define ETP_REQ eio_req + +/* + * a somewhat faster data structure might be nice, but + * with 8 priorities this actually needs <20 insns + * per shift, the most expensive operation. + */ +typedef struct { + ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */ + int size; +} etp_reqq; + +typedef struct { + etp_reqq res_queue; /* queue of outstanding responses for this channel */ + void *data; /* use this for what you want */ +} eio_channel; + /* eio request structure */ /* this structure is mostly read-only */ /* when initialising it, all members must be zero-initialised */ @@ -227,6 +249,8 @@ struct eio_req long int3; /* chown, fchown: gid */ int errorno; /* errno value on syscall return */ + eio_channel *channel; /* data used to direct poll callbacks arising from this req */ + #if __i386 || __amd64 unsigned char cancelled; #else @@ -261,11 +285,14 @@ enum { * and eio_poll_cb needs to be invoked (it MUST NOT call eio_poll_cb itself). * done_poll is called when the need to poll is gone. */ -int eio_init (void (*want_poll)(void), void (*done_poll)(void)); +int eio_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)); + +/* initialises a channel */ +void eio_channel_init(eio_channel *, void *data); /* must be called regularly to handle pending requests */ /* returns 0 if all requests were handled, -1 if not, or the value of EIO_FINISH if != 0 */ -int eio_poll (void); +int eio_poll (eio_channel *channel); /* stop polling if poll took longer than duration seconds */ void eio_set_max_poll_time (eio_tstamp nseconds); @@ -289,55 +316,55 @@ unsigned int eio_nthreads (void); /* number of worker threads in use currently * /* convenience wrappers */ #ifndef EIO_NO_WRAPPERS -eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */ -eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ties a thread for this long, simulating busyness */ -eio_req *eio_sync (int pri, eio_cb cb, void *data); -eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); -eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); -eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data); -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data); -eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data); -eio_req *eio_close (int fd, int pri, eio_cb cb, void *data); -eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); -eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data); -eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data); -eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); -eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data); -eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); -eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data); -eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data); -eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ -eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ -eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); -eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data); -eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data); +eio_req *eio_nop (int pri, eio_cb cb, void *data, eio_channel *channel); /* does nothing except go through the whole process */ +eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data, eio_channel *channel); /* ties a thread for this long, simulating busyness */ +eio_req *eio_sync (int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_close (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* result=ptr2 allocated dynamically */ +eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel); /* stat buffer=ptr2 allocated dynamically */ +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel); +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data, eio_channel *channel); #endif /*****************************************************************************/ /* groups */ -eio_req *eio_grp (eio_cb cb, void *data); +eio_req *eio_grp (eio_cb cb, void *data, eio_channel *channel); void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit); void eio_grp_limit (eio_req *grp, int limit); void eio_grp_add (eio_req *grp, eio_req *req); diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 3f6df25687..9ff318fc67 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1313,8 +1313,7 @@ UV_EXTERN uv_err_t uv_dlopen(const char* filename, uv_lib_t* library); UV_EXTERN uv_err_t uv_dlclose(uv_lib_t library); /* - * Retrieves a data pointer from a dynamic library. It is legal for a symbol to - * map to NULL. + * Retrieves a data pointer from a dynamic library. */ UV_EXTERN uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr); @@ -1401,6 +1400,8 @@ struct uv_loop_s { uv_async_t uv_eio_want_poll_notifier; uv_async_t uv_eio_done_poll_notifier; uv_idle_t uv_eio_poller; + /* Poll result queue */ + eio_channel uv_eio_channel; /* Diagnostic counters */ uv_counters_t counters; /* The last error */ diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index a024f12b86..210f8fe3f4 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -167,6 +167,7 @@ static int uv__loop_init(uv_loop_t* loop, loop->ev = ev_loop_new(EVFLAG_AUTO); #endif ev_set_userdata(loop->ev, loop); + eio_channel_init(&loop->uv_eio_channel, loop); return 0; } @@ -709,7 +710,7 @@ int uv_getaddrinfo(uv_loop_t* loop, uv_ref(loop); req = eio_custom(getaddrinfo_thread_proc, EIO_PRI_DEFAULT, - uv_getaddrinfo_done, handle); + uv_getaddrinfo_done, handle, &loop->uv_eio_channel); assert(req); assert(req->data == handle); diff --git a/deps/uv/src/unix/dl.c b/deps/uv/src/unix/dl.c index 41c244d79e..6c4ddff89e 100644 --- a/deps/uv/src/unix/dl.c +++ b/deps/uv/src/unix/dl.c @@ -25,17 +25,11 @@ #include #include -/* The dl family of functions don't set errno. We need a good way to communicate - * errors to the caller but there is only dlerror() and that returns a string - - * a string that may or may not be safe to keep a reference to... - */ -static const uv_err_t uv_inval_ = { UV_EINVAL, EINVAL }; - uv_err_t uv_dlopen(const char* filename, uv_lib_t* library) { void* handle = dlopen(filename, RTLD_LAZY); if (handle == NULL) { - return uv_inval_; + return uv__new_sys_error(errno); } *library = handle; @@ -45,7 +39,7 @@ uv_err_t uv_dlopen(const char* filename, uv_lib_t* library) { uv_err_t uv_dlclose(uv_lib_t library) { if (dlclose(library) != 0) { - return uv_inval_; + return uv__new_sys_error(errno); } return uv_ok_; @@ -53,15 +47,9 @@ uv_err_t uv_dlclose(uv_lib_t library) { uv_err_t uv_dlsym(uv_lib_t library, const char* name, void** ptr) { - void* address; - - /* Reset error status. */ - dlerror(); - - address = dlsym(library, name); - - if (dlerror()) { - return uv_inval_; + void* address = dlsym(library, name); + if (address == NULL) { + return uv__new_sys_error(errno); } *ptr = (void*) address; diff --git a/deps/uv/src/unix/eio/eio.c b/deps/uv/src/unix/eio/eio.c index 75abd9bb69..58300a65eb 100644 --- a/deps/uv/src/unix/eio/eio.c +++ b/deps/uv/src/unix/eio/eio.c @@ -362,12 +362,8 @@ static int gettimeofday(struct timeval *tv, struct timezone *tz) #define EIO_TICKS ((1000000 + 1023) >> 10) -#define ETP_PRI_MIN EIO_PRI_MIN -#define ETP_PRI_MAX EIO_PRI_MAX - struct etp_worker; -#define ETP_REQ eio_req #define ETP_DESTROY(req) eio_destroy (req) static int eio_finish (eio_req *req); #define ETP_FINISH(req) eio_finish (req) @@ -376,8 +372,6 @@ static void eio_execute (struct etp_worker *self, eio_req *req); /*****************************************************************************/ -#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) - /* calculate time difference in ~1/EIO_TICKS of a second */ ecb_inline int tvdiff (struct timeval *tv1, struct timeval *tv2) @@ -388,8 +382,8 @@ tvdiff (struct timeval *tv1, struct timeval *tv2) static unsigned int started, idle, wanted = 4; -static void (*want_poll_cb) (void); -static void (*done_poll_cb) (void); +static void (*want_poll_cb) (eio_channel *); +static void (*done_poll_cb) (eio_channel *); static unsigned int max_poll_time; /* reslock */ static unsigned int max_poll_reqs; /* reslock */ @@ -506,18 +500,8 @@ etp_nthreads (void) return retval; } -/* - * a somewhat faster data structure might be nice, but - * with 8 priorities this actually needs <20 insns - * per shift, the most expensive operation. - */ -typedef struct { - ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */ - int size; -} etp_reqq; - static etp_reqq req_queue; -static etp_reqq res_queue; +static eio_channel default_channel; static void ecb_noinline ecb_cold reqq_init (etp_reqq *q) @@ -574,7 +558,7 @@ reqq_shift (etp_reqq *q) } static int ecb_cold -etp_init (void (*want_poll)(void), void (*done_poll)(void)) +etp_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)) { X_MUTEX_CREATE (wrklock); X_MUTEX_CREATE (reslock); @@ -582,7 +566,7 @@ etp_init (void (*want_poll)(void), void (*done_poll)(void)) X_COND_CREATE (reqwait); reqq_init (&req_queue); - reqq_init (&res_queue); + eio_channel_init (&default_channel, 0); wrk_first.next = wrk_first.prev = &wrk_first; @@ -656,12 +640,19 @@ etp_end_thread (void) X_UNLOCK (wrklock); } +void +eio_channel_init(eio_channel *channel, void *data) { + reqq_init(&channel->res_queue); + channel->data = data; +} + static int -etp_poll (void) +etp_poll (eio_channel *channel) { unsigned int maxreqs; unsigned int maxtime; struct timeval tv_start, tv_now; + if(!channel) channel = &default_channel; X_LOCK (reslock); maxreqs = max_poll_reqs; @@ -678,14 +669,14 @@ etp_poll (void) etp_maybe_start_thread (); X_LOCK (reslock); - req = reqq_shift (&res_queue); + req = reqq_shift (&channel->res_queue); if (req) { --npending; - if (!res_queue.size && done_poll_cb) - done_poll_cb (); + if (!channel->res_queue.size && done_poll_cb) + done_poll_cb (channel); } X_UNLOCK (reslock); @@ -752,8 +743,8 @@ etp_submit (ETP_REQ *req) ++npending; - if (!reqq_push (&res_queue, req) && want_poll_cb) - want_poll_cb (); + if (!reqq_push (&req->channel->res_queue, req) && want_poll_cb) + want_poll_cb (req->channel); X_UNLOCK (reslock); } @@ -970,9 +961,9 @@ eio_set_max_parallel (unsigned int nthreads) etp_set_max_parallel (nthreads); } -int eio_poll (void) +int eio_poll (eio_channel *channel) { - return etp_poll (); + return etp_poll (channel); } /*****************************************************************************/ @@ -2092,8 +2083,8 @@ X_THREAD_PROC (etp_proc) ++npending; - if (!reqq_push (&res_queue, req) && want_poll_cb) - want_poll_cb (); + if (!reqq_push (&req->channel->res_queue, req) && want_poll_cb) + want_poll_cb (req->channel); self->req = 0; etp_worker_clear (self); @@ -2112,7 +2103,7 @@ quit: /*****************************************************************************/ int ecb_cold -eio_init (void (*want_poll)(void), void (*done_poll)(void)) +eio_init (void (*want_poll)(eio_channel *), void (*done_poll)(eio_channel *)) { #if !HAVE_PREADWRITE X_MUTEX_CREATE (preadwritelock); @@ -2138,7 +2129,8 @@ eio_api_destroy (eio_req *req) req->pri = pri; \ req->finish = cb; \ req->data = data; \ - req->destroy = eio_api_destroy; + req->destroy = eio_api_destroy; \ + req->channel = channel #define SEND eio_submit (req); return req @@ -2294,209 +2286,209 @@ eio_execute (etp_worker *self, eio_req *req) #ifndef EIO_NO_WRAPPERS -eio_req *eio_nop (int pri, eio_cb cb, void *data) +eio_req *eio_nop (int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_NOP); SEND; } -eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data) +eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_BUSY); req->nv1 = delay; SEND; } -eio_req *eio_sync (int pri, eio_cb cb, void *data) +eio_req *eio_sync (int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNC); SEND; } -eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSYNC); req->int1 = fd; SEND; } -eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } -eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FDATASYNC); req->int1 = fd; SEND; } -eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNCFS); req->int1 = fd; SEND; } -eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) +eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; } -eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; } -eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MLOCK); req->ptr2 = addr; req->size = length; SEND; } -eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) +eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MLOCKALL); req->int1 = flags; SEND; } -eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; } -eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_close (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CLOSE); req->int1 = fd; SEND; } -eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READAHEAD); req->int1 = fd; req->offs = offset; req->size = length; SEND; } -eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READ); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; } -eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_WRITE); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; } -eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSTAT); req->int1 = fd; SEND; } -eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) +eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FSTATVFS); req->int1 = fd; SEND; } -eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data) +eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FUTIME); req->int1 = fd; req->nv1 = atime; req->nv2 = mtime; SEND; } -eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND; } -eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_fchmod (int fd, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; } -eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) +eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_DUP2); req->int1 = fd; req->int2 = fd2; SEND; } -eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) +eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND; } -eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_open (const char *path, int flags, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND; } -eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data) +eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_UTIME); PATH; req->nv1 = atime; req->nv2 = mtime; SEND; } -eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) +eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; } -eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } -eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_chmod (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND; } -eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data) +eio_req *eio_mkdir (const char *path, eio_mode_t mode, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND; } static eio_req * -eio__1path (int type, const char *path, int pri, eio_cb cb, void *data) +eio__1path (int type, const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (type); PATH; SEND; } -eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_READLINK, path, pri, cb, data); + return eio__1path (EIO_READLINK, path, pri, cb, data, channel); } -eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_REALPATH, path, pri, cb, data); + return eio__1path (EIO_REALPATH, path, pri, cb, data, channel); } -eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_STAT, path, pri, cb, data); + return eio__1path (EIO_STAT, path, pri, cb, data, channel); } -eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_LSTAT, path, pri, cb, data); + return eio__1path (EIO_LSTAT, path, pri, cb, data, channel); } -eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_STATVFS, path, pri, cb, data); + return eio__1path (EIO_STATVFS, path, pri, cb, data, channel); } -eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_UNLINK, path, pri, cb, data); + return eio__1path (EIO_UNLINK, path, pri, cb, data, channel); } -eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__1path (EIO_RMDIR, path, pri, cb, data); + return eio__1path (EIO_RMDIR, path, pri, cb, data, channel); } -eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) +eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_READDIR); PATH; req->int1 = flags; SEND; } -eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) +eio_req *eio_mknod (const char *path, eio_mode_t mode, dev_t dev, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND; } static eio_req * -eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (type); PATH; @@ -2511,29 +2503,29 @@ eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb SEND; } -eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_LINK, path, new_path, pri, cb, data); + return eio__2path (EIO_LINK, path, new_path, pri, cb, data, channel); } -eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data); + return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data, channel); } -eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) +eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data, eio_channel *channel) { - return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); + return eio__2path (EIO_RENAME, path, new_path, pri, cb, data, channel); } -eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) +eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data, eio_channel *channel) { REQ (EIO_CUSTOM); req->feed = execute; SEND; } #endif -eio_req *eio_grp (eio_cb cb, void *data) +eio_req *eio_grp (eio_cb cb, void *data, eio_channel *channel) { const int pri = EIO_PRI_MAX; diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index 436e54c680..6615516885 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -44,7 +44,7 @@ uv_fs_req_init(loop, req, type, path, cb); \ if (cb) { \ /* async */ \ - req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req); \ + req->eio = eiofunc(args, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); \ if (!req->eio) { \ uv__set_sys_error(loop, ENOMEM); \ return -1; \ @@ -191,7 +191,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, if (cb) { /* async */ uv_ref(loop); - req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_open(path, flags, mode, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -222,7 +222,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf, /* async */ uv_ref(loop); req->eio = eio_read(fd, buf, length, offset, EIO_PRI_DEFAULT, - uv__fs_after, req); + uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); @@ -260,7 +260,7 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf, /* async */ uv_ref(loop); req->eio = eio_write(file, buf, length, offset, EIO_PRI_DEFAULT, - uv__fs_after, req); + uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -307,7 +307,7 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, if (cb) { /* async */ uv_ref(loop); - req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_readdir(path, flags, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); return -1; @@ -377,7 +377,7 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_stat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); free(pathdup); @@ -411,7 +411,7 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); @@ -550,7 +550,7 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { if (cb) { /* async */ uv_ref(loop); - req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req); + req->eio = eio_lstat(pathdup, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel); free(pathdup); @@ -598,7 +598,7 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_req_init(loop, req, UV_FS_READLINK, path, cb); if (cb) { - if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) { + if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req, &loop->uv_eio_channel))) { uv_ref(loop); return 0; } else { @@ -692,7 +692,7 @@ int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, req->work_cb = work_cb; req->after_work_cb = after_work_cb; - req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req); + req->eio = eio_custom(uv__work, EIO_PRI_DEFAULT, uv__after_work, req, &loop->uv_eio_channel); if (!req->eio) { uv__set_sys_error(loop, ENOMEM); diff --git a/deps/uv/src/unix/uv-eio.c b/deps/uv/src/unix/uv-eio.c index 84afe09b74..7c83f2c1b0 100644 --- a/deps/uv/src/unix/uv-eio.c +++ b/deps/uv/src/unix/uv-eio.c @@ -27,16 +27,12 @@ #include -/* TODO remove me! */ -static uv_loop_t* main_loop; - - static void uv_eio_do_poll(uv_idle_t* watcher, int status) { assert(watcher == &(watcher->loop->uv_eio_poller)); /* printf("uv_eio_poller\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) watcher)) { + if (eio_poll(&watcher->loop->uv_eio_channel) != -1 && uv_is_active((uv_handle_t*) watcher)) { /* printf("uv_eio_poller stop\n"); */ uv_idle_stop(watcher); uv_unref(watcher->loop); @@ -52,7 +48,7 @@ static void uv_eio_want_poll_notifier_cb(uv_async_t* watcher, int status) { /* printf("want poll notifier\n"); */ - if (eio_poll() == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { + if (eio_poll(&watcher->loop->uv_eio_channel) == -1 && !uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller start\n"); */ uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); uv_ref(loop); @@ -67,7 +63,7 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { /* printf("done poll notifier\n"); */ - if (eio_poll() != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { + if (eio_poll(&watcher->loop->uv_eio_channel) != -1 && uv_is_active((uv_handle_t*) &loop->uv_eio_poller)) { /* printf("uv_eio_poller stop\n"); */ uv_idle_stop(&loop->uv_eio_poller); uv_unref(loop); @@ -79,7 +75,7 @@ static void uv_eio_done_poll_notifier_cb(uv_async_t* watcher, int revents) { * uv_eio_want_poll() is called from the EIO thread pool each time an EIO * request (that is, one of the node.fs.* functions) has completed. */ -static void uv_eio_want_poll(void) { +static void uv_eio_want_poll(eio_channel *channel) { /* Signal the main thread that eio_poll need to be processed. */ /* @@ -87,16 +83,16 @@ static void uv_eio_want_poll(void) { * uv_eio_want_poll_notifier. */ - uv_async_send(&main_loop->uv_eio_want_poll_notifier); + uv_async_send(&((uv_loop_t *)channel->data)->uv_eio_want_poll_notifier); } -static void uv_eio_done_poll(void) { +static void uv_eio_done_poll(eio_channel *channel) { /* * Signal the main thread that we should stop calling eio_poll(). * from the idle watcher. */ - uv_async_send(&main_loop->uv_eio_done_poll_notifier); + uv_async_send(&((uv_loop_t *)channel->data)->uv_eio_done_poll_notifier); } @@ -104,8 +100,6 @@ void uv_eio_init(uv_loop_t* loop) { if (loop->counters.eio_init == 0) { loop->counters.eio_init++; - main_loop = loop; - uv_idle_init(loop, &loop->uv_eio_poller); uv_idle_start(&loop->uv_eio_poller, uv_eio_do_poll); @@ -124,11 +118,5 @@ void uv_eio_init(uv_loop_t* loop) { * race conditions. See Node's test/simple/test-eio-race.js */ eio_set_max_poll_reqs(10); - } else { - /* - * If this assertion breaks then Ryan hasn't implemented support for - * receiving thread pool requests back to multiple threads. - */ - assert(main_loop == loop); } } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index a4c13ad1aa..51b847291e 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -126,7 +126,6 @@ TEST_DECLARE (thread_self) TEST_DECLARE (strlcpy) TEST_DECLARE (strlcat) TEST_DECLARE (counters_init) - #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -293,7 +292,6 @@ TASK_LIST_START TEST_ENTRY (strlcpy) TEST_ENTRY (strlcat) TEST_ENTRY (counters_init) - #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) From 9d792f3183be5490f5c3b3d579524fdf246795b9 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Dec 2011 13:40:26 -0800 Subject: [PATCH 24/31] Add node::Loop() and don't inc node_isolate.h in *.cc node::Loop() replaces the NODE_LOOP macro. This avoids hitting v8::Isolate::GetCurrent() for each loop lookup when HAVE_ISOLATE==0 --- src/cares_wrap.cc | 10 +++++----- src/fs_event_wrap.cc | 10 +++++----- src/handle_wrap.cc | 4 ++-- src/node.cc | 19 ++++++++++++++----- src/node.h | 6 ++++-- src/node_crypto.cc | 6 +++--- src/node_file.cc | 8 ++++---- src/node_zlib.cc | 4 ++-- src/pipe_wrap.cc | 10 +++++----- src/process_wrap.cc | 8 ++++---- src/stream_wrap.cc | 16 ++++++++-------- src/tcp_wrap.cc | 28 ++++++++++++++-------------- src/timer_wrap.cc | 20 ++++++++++---------- src/tty_wrap.cc | 8 ++++---- src/udp_wrap.cc | 18 +++++++++--------- 15 files changed, 93 insertions(+), 82 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index b3e4bd50b2..64e96581b0 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -608,7 +608,7 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { if (status) { // Error - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); argv[0] = Local::New(Null()); } else { // Success @@ -711,7 +711,7 @@ static Handle GetAddrInfo(const Arguments& args) { hints.ai_family = fam; hints.ai_socktype = SOCK_STREAM; - int r = uv_getaddrinfo(NODE_LOOP(), + int r = uv_getaddrinfo(Loop(), &req_wrap->req_, AfterGetAddrInfo, *hostname, @@ -720,7 +720,7 @@ static Handle GetAddrInfo(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -737,7 +737,7 @@ static void Initialize(Handle target) { assert(r == ARES_SUCCESS); struct ares_options options; - uv_ares_init_options(NODE_LOOP(), &ares_channel, &options, 0); + uv_ares_init_options(Loop(), &ares_channel, &options, 0); assert(r == 0); NODE_SET_METHOD(target, "queryA", Query); diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 2e6dee4c47..1f99e856a8 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include @@ -110,15 +110,15 @@ Handle FSEventWrap::Start(const Arguments& args) { String::Utf8Value path(args[0]->ToString()); - int r = uv_fs_event_init(NODE_LOOP(), &wrap->handle_, *path, OnEvent, 0); + int r = uv_fs_event_init(Loop(), &wrap->handle_, *path, OnEvent, 0); if (r == 0) { // Check for persistent argument if (!args[1]->IsTrue()) { - uv_unref(NODE_LOOP()); + uv_unref(Loop()); } wrap->initialized_ = true; } else { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } return scope.Close(Integer::New(r)); @@ -146,7 +146,7 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, // assumption that a rename implicitly means an attribute change. Not too // unreasonable, right? Still, we should revisit this before v1.0. if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); eventStr = String::Empty(); } else if (events & UV_RENAME) { diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 2aff643eb3..f661885d9f 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include namespace node { @@ -71,7 +71,7 @@ Handle HandleWrap::Unref(const Arguments& args) { } wrap->unref = true; - uv_unref(NODE_LOOP()); + uv_unref(Loop()); return v8::Undefined(); } diff --git a/src/node.cc b/src/node.cc index 61b4547c34..548fdc7b3d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -147,6 +147,15 @@ void StartThread(Isolate* isolate, char** argv); +uv_loop_t* Loop() { +#if defined(HAVE_ISOLATES) && HAVE_ISOLATES + return Isolate::GetCurrent()->GetLoop(); +#else + return uv_default_loop(); +#endif +} + + static void StartGCTimer () { if (!uv_is_active((uv_handle_t*) &gc_timer)) { uv_timer_start(&gc_timer, node::CheckStatus, 5000, 5000); @@ -173,7 +182,7 @@ static void Idle(uv_idle_t* watcher, int status) { static void Check(uv_check_t* watcher, int status) { assert(watcher == &gc_check); - tick_times[tick_time_head] = uv_now(NODE_LOOP()); + tick_times[tick_time_head] = uv_now(Loop()); tick_time_head = (tick_time_head + 1) % RPM_SAMPLES; StartGCTimer(); @@ -203,7 +212,7 @@ static void Tick(void) { need_tick_cb = false; if (uv_is_active((uv_handle_t*) &tick_spinner)) { uv_idle_stop(&tick_spinner); - uv_unref(NODE_LOOP()); + uv_unref(Loop()); } HandleScope scope; @@ -245,7 +254,7 @@ static Handle NeedTickCallback(const Arguments& args) { // tick_spinner to keep the event loop alive long enough to handle it. if (!uv_is_active((uv_handle_t*) &tick_spinner)) { uv_idle_start(&tick_spinner, Spin); - uv_ref(NODE_LOOP()); + uv_ref(Loop()); } return Undefined(); } @@ -1497,7 +1506,7 @@ static void CheckStatus(uv_timer_t* watcher, int status) { } } - double d = uv_now(NODE_LOOP()) - TICK_TIME(3); + double d = uv_now(Loop()) - TICK_TIME(3); //printfb("timer d = %f\n", d); @@ -1526,7 +1535,7 @@ static Handle Uptime(const Arguments& args) { v8::Handle UVCounters(const v8::Arguments& args) { HandleScope scope; - uv_counters_t* c = &NODE_LOOP()->counters; + uv_counters_t* c = &Loop()->counters; Local obj = Object::New(); diff --git a/src/node.h b/src/node.h index e8c532464e..f17c3fcf81 100644 --- a/src/node.h +++ b/src/node.h @@ -75,8 +75,6 @@ #define NODE_STRINGIFY_HELPER(n) #n #endif -#define NODE_LOOP() (node::Isolate::GetCurrent()->GetLoop()) - namespace node { int Start(int argc, char *argv[]); @@ -86,6 +84,10 @@ v8::Handle SetupProcessObject(int argc, char *argv[]); void Load(v8::Handle process); void EmitExit(v8::Handle process); +// Returns the loop for the current isolate. If compiled with +// --without-isolates then this will always return uv_default_loop(); +uv_loop_t* Loop(); + #define NODE_PSYMBOL(s) \ v8::Persistent::New(v8::String::NewSymbol(s)) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 9cb7280771..bcd3949ec0 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -4118,7 +4118,7 @@ PBKDF2(const Arguments& args) { req = new uv_work_t(); req->data = request; - uv_queue_work(NODE_LOOP(), req, EIO_PBKDF2, EIO_PBKDF2After); + uv_queue_work(Loop(), req, EIO_PBKDF2, EIO_PBKDF2After); return Undefined(); @@ -4241,7 +4241,7 @@ Handle RandomBytes(const Arguments& args) { Local callback_v = Local(Function::Cast(*args[1])); req->callback_ = Persistent::New(callback_v); - uv_queue_work(NODE_LOOP(), + uv_queue_work(Loop(), &req->work_req_, RandomBytesWork, RandomBytesAfter); diff --git a/src/node_file.cc b/src/node_file.cc index e7293fb00e..f7d2d87ed1 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -22,7 +22,7 @@ #include "node.h" #include "node_file.h" #include "node_buffer.h" -#include +#include #ifdef __POSIX__ # include "node_stat_watcher.h" #endif @@ -226,7 +226,7 @@ struct fs_req_wrap { #define ASYNC_CALL(func, callback, ...) \ FSReqWrap* req_wrap = new FSReqWrap(); \ - int r = uv_fs_##func(NODE_LOOP(), &req_wrap->req_, \ + int r = uv_fs_##func(Loop(), &req_wrap->req_, \ __VA_ARGS__, After); \ assert(r == 0); \ req_wrap->object_->Set(oncomplete_sym, callback); \ @@ -235,9 +235,9 @@ struct fs_req_wrap { #define SYNC_CALL(func, path, ...) \ fs_req_wrap req_wrap; \ - int result = uv_fs_##func(NODE_LOOP(), &req_wrap.req, __VA_ARGS__, NULL); \ + int result = uv_fs_##func(Loop(), &req_wrap.req, __VA_ARGS__, NULL); \ if (result < 0) { \ - int code = uv_last_error(NODE_LOOP()).code; \ + int code = uv_last_error(Loop()).code; \ return ThrowException(UVException(code, #func, "", path)); \ } diff --git a/src/node_zlib.cc b/src/node_zlib.cc index eb7d06d9c4..228427b640 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include @@ -134,7 +134,7 @@ template class ZCtx : public ObjectWrap { uv_work_t* work_req = new uv_work_t(); work_req->data = req_wrap; - uv_queue_work(NODE_LOOP(), + uv_queue_work(Loop(), work_req, ZCtx::Process, ZCtx::After); diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 483118ad84..c33de3858f 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -124,7 +124,7 @@ Handle PipeWrap::New(const Arguments& args) { PipeWrap::PipeWrap(Handle object, bool ipc) : StreamWrap(object, (uv_stream_t*) &handle_) { - int r = uv_pipe_init(NODE_LOOP(), &handle_, ipc); + int r = uv_pipe_init(Loop(), &handle_, ipc); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_pipe_init() returns void. handle_.data = reinterpret_cast(this); @@ -142,7 +142,7 @@ Handle PipeWrap::Bind(const Arguments& args) { int r = uv_pipe_bind(&wrap->handle_, *name); // Error starting the pipe. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -173,7 +173,7 @@ Handle PipeWrap::Listen(const Arguments& args) { int r = uv_listen((uv_stream_t*)&wrap->handle_, backlog, OnConnection); // Error starting the pipe. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -226,7 +226,7 @@ void PipeWrap::AfterConnect(uv_connect_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } Local argv[3] = { diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 3d988ac1f5..847a76352f 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -176,7 +176,7 @@ class ProcessWrap : public HandleWrap { Get(String::NewSymbol("windowsVerbatimArguments"))->IsTrue(); #endif - int r = uv_spawn(NODE_LOOP(), &wrap->process_, options); + int r = uv_spawn(Loop(), &wrap->process_, options); wrap->SetHandle((uv_handle_t*)&wrap->process_); assert(wrap->process_.data == wrap); @@ -196,7 +196,7 @@ class ProcessWrap : public HandleWrap { delete [] options.env; } - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -210,7 +210,7 @@ class ProcessWrap : public HandleWrap { int r = uv_process_kill(&wrap->process_, signal); - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index fbe9152b5a..a64b4c356c 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -133,7 +133,7 @@ Handle StreamWrap::ReadStart(const Arguments& args) { } // Error starting the tcp. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -147,7 +147,7 @@ Handle StreamWrap::ReadStop(const Arguments& args) { int r = uv_read_stop(wrap->stream_); // Error starting the tcp. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -226,7 +226,7 @@ void StreamWrap::OnReadCommon(uv_stream_t* handle, ssize_t nread, slab_used -= buf.len; } - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); MakeCallback(wrap->object_, "onread", 0, NULL); return; } @@ -339,7 +339,7 @@ Handle StreamWrap::Write(const Arguments& args) { wrap->UpdateWriteQueueSize(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -359,7 +359,7 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } wrap->UpdateWriteQueueSize(); @@ -389,7 +389,7 @@ Handle StreamWrap::Shutdown(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -409,7 +409,7 @@ void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { HandleScope scope; if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } Local argv[3] = { diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 83fdcdd14a..2871025d76 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -155,7 +155,7 @@ Handle TCPWrap::New(const Arguments& args) { TCPWrap::TCPWrap(Handle object) : StreamWrap(object, (uv_stream_t*) &handle_) { - int r = uv_tcp_init(NODE_LOOP(), &handle_); + int r = uv_tcp_init(Loop(), &handle_); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_tcp_init() returns void. UpdateWriteQueueSize(); @@ -183,7 +183,7 @@ Handle TCPWrap::GetSockName(const Arguments& args) { Local sockname = Object::New(); if (r != 0) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } else { family = address.ss_family; @@ -225,7 +225,7 @@ Handle TCPWrap::GetPeerName(const Arguments& args) { Local sockname = Object::New(); if (r != 0) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } else { family = address.ss_family; @@ -258,7 +258,7 @@ Handle TCPWrap::SetNoDelay(const Arguments& args) { int r = uv_tcp_nodelay(&wrap->handle_, 1); if (r) - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return Undefined(); } @@ -274,7 +274,7 @@ Handle TCPWrap::SetKeepAlive(const Arguments& args) { int r = uv_tcp_keepalive(&wrap->handle_, enable, delay); if (r) - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return Undefined(); } @@ -290,7 +290,7 @@ Handle TCPWrap::SetSimultaneousAccepts(const Arguments& args) { int r = uv_tcp_simultaneous_accepts(&wrap->handle_, enable ? 1 : 0); if (r) - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return Undefined(); } @@ -309,7 +309,7 @@ Handle TCPWrap::Bind(const Arguments& args) { int r = uv_tcp_bind(&wrap->handle_, address); // Error starting the tcp. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -327,7 +327,7 @@ Handle TCPWrap::Bind6(const Arguments& args) { int r = uv_tcp_bind6(&wrap->handle_, address); // Error starting the tcp. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -343,7 +343,7 @@ Handle TCPWrap::Listen(const Arguments& args) { int r = uv_listen((uv_stream_t*)&wrap->handle_, backlog, OnConnection); // Error starting the tcp. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -378,7 +378,7 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) { // Successful accept. Call the onconnection callback in JavaScript land. argv[0] = client_obj; } else { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); argv[0] = v8::Null(); } @@ -397,7 +397,7 @@ void TCPWrap::AfterConnect(uv_connect_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } Local argv[3] = { @@ -433,7 +433,7 @@ Handle TCPWrap::Connect(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return scope.Close(v8::Null()); } else { @@ -460,7 +460,7 @@ Handle TCPWrap::Connect6(const Arguments& args) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return scope.Close(v8::Null()); } else { diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 82b7ec984e..2b29e5f854 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #define UNWRAP \ assert(!args.Holder().IsEmpty()); \ @@ -92,7 +92,7 @@ class TimerWrap : public HandleWrap { : HandleWrap(object, (uv_handle_t*) &handle_) { active_ = false; - int r = uv_timer_init(NODE_LOOP(), &handle_); + int r = uv_timer_init(Loop(), &handle_); assert(r == 0); handle_.data = this; @@ -100,11 +100,11 @@ class TimerWrap : public HandleWrap { // uv_timer_init adds a loop reference. (That is, it calls uv_ref.) This // is not the behavior we want in Node. Timers should not increase the // ref count of the loop except when active. - uv_unref(NODE_LOOP()); + uv_unref(Loop()); } ~TimerWrap() { - if (!active_) uv_ref(NODE_LOOP()); + if (!active_) uv_ref(Loop()); } void StateChange() { @@ -114,11 +114,11 @@ class TimerWrap : public HandleWrap { if (!was_active && active_) { // If our state is changing from inactive to active, we // increase the loop's reference count. - uv_ref(NODE_LOOP()); + uv_ref(Loop()); } else if (was_active && !active_) { // If our state is changing from active to inactive, we // decrease the loop's reference count. - uv_unref(NODE_LOOP()); + uv_unref(Loop()); } } @@ -133,7 +133,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_start(&wrap->handle_, OnTimeout, timeout, repeat); // Error starting the timer. - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); wrap->StateChange(); @@ -147,7 +147,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_stop(&wrap->handle_); - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); wrap->StateChange(); @@ -161,7 +161,7 @@ class TimerWrap : public HandleWrap { int r = uv_timer_again(&wrap->handle_); - if (r) SetErrno(uv_last_error(NODE_LOOP())); + if (r) SetErrno(uv_last_error(Loop())); wrap->StateChange(); @@ -187,7 +187,7 @@ class TimerWrap : public HandleWrap { int64_t repeat = uv_timer_get_repeat(&wrap->handle_); - if (repeat < 0) SetErrno(uv_last_error(NODE_LOOP())); + if (repeat < 0) SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(repeat)); } diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 1ec8410f16..31949011f3 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -125,7 +125,7 @@ class TTYWrap : StreamWrap { int r = uv_tty_get_winsize(&wrap->handle_, &width, &height); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return v8::Undefined(); } @@ -144,7 +144,7 @@ class TTYWrap : StreamWrap { int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue()); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } return scope.Close(Integer::New(r)); @@ -170,7 +170,7 @@ class TTYWrap : StreamWrap { TTYWrap(Handle object, int fd, bool readable) : StreamWrap(object, (uv_stream_t*)&handle_) { - uv_tty_init(NODE_LOOP(), &handle_, fd, readable); + uv_tty_init(Loop(), &handle_, fd, readable); } uv_tty_t handle_; diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index f0c487eced..dce45ada49 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -104,7 +104,7 @@ private: UDPWrap::UDPWrap(Handle object): HandleWrap(object, (uv_handle_t*)&handle_) { - int r = uv_udp_init(NODE_LOOP(), &handle_); + int r = uv_udp_init(Loop(), &handle_); assert(r == 0); // can't fail anyway handle_.data = reinterpret_cast(this); } @@ -177,7 +177,7 @@ Handle UDPWrap::DoBind(const Arguments& args, int family) { } if (r) - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return scope.Close(Integer::New(r)); } @@ -234,7 +234,7 @@ Handle UDPWrap::DoSend(const Arguments& args, int family) { req_wrap->Dispatched(); if (r) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); delete req_wrap; return Null(); } @@ -261,8 +261,8 @@ Handle UDPWrap::RecvStart(const Arguments& args) { // UV_EALREADY means that the socket is already bound but that's okay int r = uv_udp_recv_start(&wrap->handle_, OnAlloc, OnRecv); - if (r && uv_last_error(NODE_LOOP()).code != UV_EALREADY) { - SetErrno(uv_last_error(NODE_LOOP())); + if (r && uv_last_error(Loop()).code != UV_EALREADY) { + SetErrno(uv_last_error(Loop())); return False(); } @@ -298,7 +298,7 @@ Handle UDPWrap::GetSockName(const Arguments& args) { return scope.Close(sockname); } else { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); return Null(); } } @@ -317,7 +317,7 @@ void UDPWrap::OnSend(uv_udp_send_t* req, int status) { assert(wrap->object_.IsEmpty() == false); if (status) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } Local argv[4] = { @@ -365,7 +365,7 @@ void UDPWrap::OnRecv(uv_udp_t* handle, }; if (nread == -1) { - SetErrno(uv_last_error(NODE_LOOP())); + SetErrno(uv_last_error(Loop())); } else { Local rinfo = Object::New(); From 533a4552745d24ab61fb1e0b8617903f19557046 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 23 Dec 2011 14:06:32 -0800 Subject: [PATCH 25/31] move isolate V8 functions out of node.cc --- src/node.cc | 85 +--------------------- src/node_extensions.h | 4 ++ src/node_internals.h | 7 ++ src/node_isolate.cc | 113 +++++++++++++++++++++++++++++- test/addons/shared-buffer/test.js | 3 +- test/simple/test-isolates.js | 9 +-- 6 files changed, 130 insertions(+), 91 deletions(-) diff --git a/src/node.cc b/src/node.cc index 548fdc7b3d..4cedfbc018 100644 --- a/src/node.cc +++ b/src/node.cc @@ -21,6 +21,7 @@ #include #include +#include #include @@ -142,10 +143,6 @@ static bool print_eval; static void CheckStatus(uv_timer_t* watcher, int status); -void StartThread(Isolate* isolate, - int argc, - char** argv); - uv_loop_t* Loop() { #if defined(HAVE_ISOLATES) && HAVE_ISOLATES @@ -1859,82 +1856,6 @@ static Handle Binding(const Arguments& args) { } -static void RunIsolate(void* arg) { - node::Isolate* isolate = reinterpret_cast(arg); - isolate->Enter(); - StartThread(isolate, isolate->argc_, isolate->argv_); - isolate->Dispose(); - delete isolate; -} - - -static char magic_isolate_cookie_[] = "magic isolate cookie"; - - -static Handle NewIsolate(const Arguments& args) { - HandleScope scope; - - assert(args[0]->IsArray()); - - Local argv = args[0].As(); - 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(); - - // 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 (uv_thread_create(&isolate->tid_, RunIsolate, isolate)) { - delete isolate; - return Null(); - } - - Local tpl = ObjectTemplate::New(); - tpl->SetInternalFieldCount(2); - - Local obj = tpl->NewInstance(); - obj->SetPointerInInternalField(0, magic_isolate_cookie_); - obj->SetPointerInInternalField(1, isolate); - - return scope.Close(obj); -} - - -static Handle CountIsolate(const Arguments& args) { - HandleScope scope; - return scope.Close(Integer::New(Isolate::Count())); -} - - -static Handle JoinIsolate(const Arguments& args) { - HandleScope scope; - - assert(args[0]->IsObject()); - - Local obj = args[0]->ToObject(); - assert(obj->InternalFieldCount() == 2); - assert(obj->GetPointerFromInternalField(0) == magic_isolate_cookie_); - - Isolate* ti = reinterpret_cast( - obj->GetPointerFromInternalField(1)); - - if (uv_thread_join(&ti->tid_)) - return False(); // error - else - return True(); // ok -} - - static Handle ProcessTitleGetter(Local property, const AccessorInfo& info) { HandleScope scope; @@ -2206,10 +2127,6 @@ Handle SetupProcessObject(int argc, char *argv[]) { NODE_SET_METHOD(process, "binding", Binding); - NODE_SET_METHOD(process, "_newIsolate", NewIsolate); - NODE_SET_METHOD(process, "_countIsolate", CountIsolate); - NODE_SET_METHOD(process, "_joinIsolate", JoinIsolate); - return process; } diff --git a/src/node_extensions.h b/src/node_extensions.h index 39ddf1748f..1d5b132238 100644 --- a/src/node_extensions.h +++ b/src/node_extensions.h @@ -34,6 +34,10 @@ NODE_EXT_LIST_ITEM(node_signal_watcher) NODE_EXT_LIST_ITEM(node_os) NODE_EXT_LIST_ITEM(node_zlib) +#if defined(HAVE_ISOLATES) && HAVE_ISOLATES +NODE_EXT_LIST_ITEM(node_isolates) +#endif + // libuv rewrite NODE_EXT_LIST_ITEM(node_timer_wrap) NODE_EXT_LIST_ITEM(node_tcp_wrap) diff --git a/src/node_internals.h b/src/node_internals.h index 8088e229ec..08dbcb1f43 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -24,6 +24,13 @@ namespace node { +// This function starts an Isolate. This function is defined in node.cc +// currently so that we minimize the diff between master and v0.6 for easy +// merging. In the future, when v0.6 is extinct, StartThread should be moved +// to node_isolate.cc. +class Isolate; +void StartThread(Isolate* isolate, int argc, char** argv); + #ifndef offset_of // g++ in strict mode complains loudly about the system offsetof() macro // because it uses NULL as the base address. diff --git a/src/node_isolate.cc b/src/node_isolate.cc index e2d39222b9..a05cbea368 100644 --- a/src/node_isolate.cc +++ b/src/node_isolate.cc @@ -19,16 +19,34 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node_isolate.h" +#include +#include +#include +#include #include #include #include - namespace node { +using v8::Arguments; +using v8::Array; +using v8::False; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Null; +using v8::Object; +using v8::ObjectTemplate; +using v8::String; +using v8::True; +using v8::Value; + +static char magic_isolate_cookie_[] = "magic isolate cookie"; + static volatile bool initialized; static volatile int id; @@ -166,4 +184,95 @@ void Isolate::Dispose() { } +static void RunIsolate(void* arg) { + node::Isolate* isolate = reinterpret_cast(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; +} + + +static Handle CreateIsolate(const Arguments& args) { + HandleScope scope; + + assert(args[0]->IsArray()); + + Local argv = args[0].As(); + 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(); + + // 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 (uv_thread_create(&isolate->tid_, RunIsolate, isolate)) { + delete isolate; + return Null(); + } + + // TODO instead of ObjectTemplate - have a special wrapper. + Local tpl = ObjectTemplate::New(); + tpl->SetInternalFieldCount(2); + + Local obj = tpl->NewInstance(); + obj->SetPointerInInternalField(0, magic_isolate_cookie_); + obj->SetPointerInInternalField(1, isolate); + + return scope.Close(obj); +} + + +static Handle CountIsolate(const Arguments& args) { + HandleScope scope; + return scope.Close(Integer::New(Isolate::Count())); +} + + +static Handle JoinIsolate(const Arguments& args) { + HandleScope scope; + + assert(args[0]->IsObject()); + + Local obj = args[0]->ToObject(); + assert(obj->InternalFieldCount() == 2); + assert(obj->GetPointerFromInternalField(0) == magic_isolate_cookie_); + + Isolate* ti = reinterpret_cast( + obj->GetPointerFromInternalField(1)); + + if (uv_thread_join(&ti->tid_)) + return False(); // error + else + return True(); // ok +} + + +void InitIsolates(Handle target) { + HandleScope scope; + NODE_SET_METHOD(target, "create", CreateIsolate); + NODE_SET_METHOD(target, "count", CountIsolate); + NODE_SET_METHOD(target, "join", JoinIsolate); +} + + } // namespace node + + +NODE_MODULE(node_isolates, node::InitIsolates) diff --git a/test/addons/shared-buffer/test.js b/test/addons/shared-buffer/test.js index ab7da43bd6..9ba896f28a 100644 --- a/test/addons/shared-buffer/test.js +++ b/test/addons/shared-buffer/test.js @@ -1,10 +1,11 @@ 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 = process._newIsolate(process.argv); + var isolate = isolates.create(process.argv); for (var i = 0; i < binding.length; i++) { console.log('parent', 'binding.set(' + i + ', ' + i + ')', diff --git a/test/simple/test-isolates.js b/test/simple/test-isolates.js index 810ae95b8d..fa2dccd330 100644 --- a/test/simple/test-isolates.js +++ b/test/simple/test-isolates.js @@ -1,10 +1,11 @@ var fs = require('fs'); var http = require('http'); +var isolates = process.binding('isolates'); -console.log("count: %d", process._countIsolate()); +console.log("count: %d", isolates.count()); if (process.tid === 1) { - var isolate = process._newIsolate(process.argv); + var isolate = isolates.create(process.argv); //process._joinIsolate(isolate); console.error("master"); fs.stat(__dirname, function(err, stat) { @@ -19,7 +20,7 @@ if (process.tid === 1) { }); }, 500); - console.log("thread 1 count: %d", process._countIsolate()); + console.log("thread 1 count: %d", isolates.count()); } else { console.error("slave"); fs.stat(__dirname, function(err, stat) { @@ -34,5 +35,5 @@ if (process.tid === 1) { }); }, 500); - console.error("thread 2 count: %d", process._countIsolate()); + console.error("thread 2 count: %d", isolates.count()); } From 036e59394a0d8055c19174e8834128712e6da9f4 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 27 Dec 2011 13:06:25 -0800 Subject: [PATCH 26/31] Add process.features.isolates --- src/node.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/node.cc b/src/node.cc index 4cedfbc018..94b8c44680 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1986,6 +1986,15 @@ static Handle GetFeatures() { obj->Set(String::NewSymbol("tls"), Boolean::New(get_builtin_module("crypto") != NULL)); + + obj->Set(String::NewSymbol("isolates"), +#if HAVE_ISOLATES + True() +#else + False() +#endif + ); + return scope.Close(obj); } From b31969913240ab5d8fc2d570202ee86f0500f28f Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 27 Dec 2011 18:11:32 -0800 Subject: [PATCH 27/31] Add isolate version of test-child-process-fork --- lib/child_process.js | 6 ++++++ test/simple/test-child-process-fork.js | 8 +++++++- test/simple/test-isolates2.js | 13 +++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-isolates2.js diff --git a/lib/child_process.js b/lib/child_process.js index cef093a0b7..b3e6383c75 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -153,6 +153,12 @@ exports.fork = function(modulePath, args, options) { args = args ? args.slice(0) : []; args.unshift(modulePath); + if (options.thread) { + if (!process.features.isolates) { + throw new Error('node compiled without isolate support'); + } + } + if (options.stdinStream) { throw new Error('stdinStream not allowed for fork()'); } diff --git a/test/simple/test-child-process-fork.js b/test/simple/test-child-process-fork.js index 41cc28c72e..ea99ae7704 100644 --- a/test/simple/test-child-process-fork.js +++ b/test/simple/test-child-process-fork.js @@ -24,7 +24,13 @@ var common = require('../common'); var fork = require('child_process').fork; var args = ['foo', 'bar']; -var n = fork(common.fixturesDir + '/child-process-spawn-node.js', args); +var options = { + thread: process.TEST_ISOLATE ? true : false +}; + +var n = fork(common.fixturesDir + '/child-process-spawn-node.js', + args, + options); assert.deepEqual(args, ['foo', 'bar']); var messageCount = 0; diff --git a/test/simple/test-isolates2.js b/test/simple/test-isolates2.js new file mode 100644 index 0000000000..1823b8fe76 --- /dev/null +++ b/test/simple/test-isolates2.js @@ -0,0 +1,13 @@ +// 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); From 59faab4330c2c07eed57232cde03d0484751c23b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 28 Dec 2011 23:56:53 +0100 Subject: [PATCH 28/31] Make msbuild run in parallel. --- vcbuild.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vcbuild.bat b/vcbuild.bat index 38a1951b92..952c043e48 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -76,7 +76,7 @@ goto run :msbuild-found @rem Build the sln with msbuild. -msbuild node.sln /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +msbuild node.sln /m /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto msi @@ -89,7 +89,7 @@ python "%~dp0tools\getnodeversion.py" > "%temp%\node_version.txt" if not errorlevel 0 echo Cannot determine current version of node.js & goto exit for /F "tokens=*" %%i in (%temp%\node_version.txt) do set NODE_VERSION=%%i heat dir deps\npm -var var.NPMSourceDir -dr NodeModulesFolder -cg NPMFiles -gg -template fragment -nologo -out npm.wxs -msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /t:Clean,Build /p:Configuration=%config% /p:NodeVersion=%NODE_VERSION% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build /p:Configuration=%config% /p:NodeVersion=%NODE_VERSION% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if defined nosign goto run From 2afd20b5425e62c3019278d6682b62171664f456 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 29 Dec 2011 00:18:23 +0100 Subject: [PATCH 29/31] uv: upgrade to 85f6b79 --- deps/uv/include/uv-private/uv-unix.h | 2 + deps/uv/include/uv.h | 2 - deps/uv/src/unix/uv-eio.c | 19 ++-- deps/uv/test/test-list.h | 2 + deps/uv/test/test-thread.c | 133 +++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 8 deletions(-) diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 99537347f7..24ef37cb9d 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -63,6 +63,8 @@ typedef void* uv_lib_t; * definition of ares_timeout(). \ */ \ ev_timer timer; \ + /* Poll result queue */ \ + eio_channel uv_eio_channel; \ struct ev_loop* ev; #define UV_REQ_BUFSML_SIZE (4) diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 9ff318fc67..7e089ef724 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1400,8 +1400,6 @@ struct uv_loop_s { uv_async_t uv_eio_want_poll_notifier; uv_async_t uv_eio_done_poll_notifier; uv_idle_t uv_eio_poller; - /* Poll result queue */ - eio_channel uv_eio_channel; /* Diagnostic counters */ uv_counters_t counters; /* The last error */ diff --git a/deps/uv/src/unix/uv-eio.c b/deps/uv/src/unix/uv-eio.c index 7c83f2c1b0..8656ea6f5a 100644 --- a/deps/uv/src/unix/uv-eio.c +++ b/deps/uv/src/unix/uv-eio.c @@ -96,6 +96,18 @@ static void uv_eio_done_poll(eio_channel *channel) { } +static void uv__eio_init(void) { + eio_init(uv_eio_want_poll, uv_eio_done_poll); + /* + * Don't handle more than 10 reqs on each eio_poll(). This is to avoid + * race conditions. See Node's test/simple/test-eio-race.js + */ + eio_set_max_poll_reqs(10); +} + +static uv_once_t uv__eio_init_once_guard = UV_ONCE_INIT; + + void uv_eio_init(uv_loop_t* loop) { if (loop->counters.eio_init == 0) { loop->counters.eio_init++; @@ -112,11 +124,6 @@ void uv_eio_init(uv_loop_t* loop) { uv_eio_done_poll_notifier_cb); uv_unref(loop); - eio_init(uv_eio_want_poll, uv_eio_done_poll); - /* - * Don't handle more than 10 reqs on each eio_poll(). This is to avoid - * race conditions. See Node's test/simple/test-eio-race.js - */ - eio_set_max_poll_reqs(10); + uv_once(&uv__eio_init_once_guard, uv__eio_init); } } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 51b847291e..f5f0541269 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -119,6 +119,7 @@ TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_open_dir) TEST_DECLARE (threadpool_queue_work_simple) +TEST_DECLARE (threadpool_multiple_event_loops) TEST_DECLARE (thread_mutex) TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_create) @@ -285,6 +286,7 @@ TASK_LIST_START TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_open_dir) TEST_ENTRY (threadpool_queue_work_simple) + TEST_ENTRY (threadpool_multiple_event_loops) TEST_ENTRY (thread_mutex) TEST_ENTRY (thread_rwlock) TEST_ENTRY (thread_create) diff --git a/deps/uv/test/test-thread.c b/deps/uv/test/test-thread.c index 48b31b172e..5c0bb75e61 100644 --- a/deps/uv/test/test-thread.c +++ b/deps/uv/test/test-thread.c @@ -23,12 +23,123 @@ #include "task.h" #include +#include #include +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +struct getaddrinfo_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_getaddrinfo_t handle; +}; + + +struct fs_req { + uv_thread_t thread_id; + unsigned int counter; + uv_loop_t* loop; + uv_fs_t handle; +}; + +static void getaddrinfo_do(struct getaddrinfo_req* req); +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res); +static void fs_do(struct fs_req* req); +static void fs_cb(uv_fs_t* handle); static volatile int thread_called; +static void getaddrinfo_do(struct getaddrinfo_req* req) { + int r; + + ASSERT(req->thread_id == uv_thread_self()); + + r = uv_getaddrinfo(req->loop, + &req->handle, + getaddrinfo_cb, + "localhost", + NULL, + NULL); + ASSERT(r == 0); +} + + +static void getaddrinfo_cb(uv_getaddrinfo_t* handle, + int status, + struct addrinfo* res) { + struct getaddrinfo_req* req; + + ASSERT(status == 0); + + req = container_of(handle, struct getaddrinfo_req, handle); + uv_freeaddrinfo(res); + + if (--req->counter) + getaddrinfo_do(req); +} + + +static void fs_do(struct fs_req* req) { + int r; + + ASSERT(req->thread_id == uv_thread_self()); + + r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb); + ASSERT(r == 0); +} + + +static void fs_cb(uv_fs_t* handle) { + struct fs_req* req = container_of(handle, struct fs_req, handle); + + if (--req->counter) + fs_do(req); +} + + +static void do_work(void* arg) { + struct getaddrinfo_req getaddrinfo_reqs[16]; + struct fs_req fs_reqs[16]; + uv_thread_t self; + uv_loop_t* loop; + size_t i; + int r; + + self = uv_thread_self(); + + loop = uv_loop_new(); + ASSERT(loop != NULL); + + for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) { + struct getaddrinfo_req* req = getaddrinfo_reqs + i; + req->thread_id = self; + req->counter = 16; + req->loop = loop; + getaddrinfo_do(req); + } + + for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) { + struct fs_req* req = fs_reqs + i; + req->thread_id = self; + req->counter = 16; + req->loop = loop; + fs_do(req); + } + + r = uv_run(loop); + ASSERT(r == 0); + + uv_loop_delete(loop); +} + + static void thread_entry(void* arg) { ASSERT(arg == (void *) 42); thread_called++; @@ -56,3 +167,25 @@ TEST_IMPL(thread_self) { tid = uv_thread_self(); return 0; } + + +/* Hilariously bad test name. Run a lot of tasks in the thread pool and verify + * that each "finished" callback is run in its originating thread. + */ +TEST_IMPL(threadpool_multiple_event_loops) { + uv_thread_t threads[8]; + size_t i; + int r; + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_create(threads + i, do_work, NULL); + ASSERT(r == 0); + } + + for (i = 0; i < ARRAY_SIZE(threads); i++) { + r = uv_thread_join(threads + i); + ASSERT(r == 0); + } + + return 0; +} From 9143b43e98ce2ea3d8eea49cc0f171f53ecf7029 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 29 Dec 2011 00:40:40 +0100 Subject: [PATCH 30/31] Include ngx-queue.h, fix Windows build. --- src/ngx-queue.h | 106 +++++++++++++++++++++++++++++++++++++++++++++ src/node_isolate.h | 1 + 2 files changed, 107 insertions(+) create mode 100644 src/ngx-queue.h diff --git a/src/ngx-queue.h b/src/ngx-queue.h new file mode 100644 index 0000000000..8c5e461762 --- /dev/null +++ b/src/ngx-queue.h @@ -0,0 +1,106 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_QUEUE_H_INCLUDED_ +#define _NGX_QUEUE_H_INCLUDED_ + + +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_ */ diff --git a/src/node_isolate.h b/src/node_isolate.h index e2dac08880..a80e6ac1d2 100644 --- a/src/node_isolate.h +++ b/src/node_isolate.h @@ -25,6 +25,7 @@ #include "v8.h" #include "uv.h" #include "node_vars.h" +#include "ngx-queue.h" #ifdef NDEBUG # define NODE_ISOLATE_CHECK(ptr) ((void) (ptr)) From 5427311ae6800e0cdcbf1dfb352c084fd01d2b58 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 29 Dec 2011 01:42:18 +0100 Subject: [PATCH 31/31] uv: upgrade to 0db56ea --- deps/uv/src/win/thread.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c index d6d3ce8f39..05bff8b917 100644 --- a/deps/uv/src/win/thread.c +++ b/deps/uv/src/win/thread.c @@ -113,6 +113,11 @@ int uv_thread_join(uv_thread_t *tid) { } +uv_thread_t uv_thread_self(void) { + return GetCurrentThreadId(); +} + + int uv_mutex_init(uv_mutex_t* mutex) { InitializeCriticalSection(mutex); return 0;