From 00fda0e7788f1292ad6c46c29fd1679ed52b364f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 22 Jul 2013 14:58:25 +0800 Subject: [PATCH] Move common message integration code to NodeBindings' base class. --- common/node_bindings.cc | 94 +++++++++++++++++++++++++- common/node_bindings.h | 43 +++++++++++- common/node_bindings_mac.cc | 128 +++++++----------------------------- common/node_bindings_mac.h | 38 +---------- 4 files changed, 157 insertions(+), 146 deletions(-) diff --git a/common/node_bindings.cc b/common/node_bindings.cc index 6b351e367b3f..fb0c2f63e2c9 100644 --- a/common/node_bindings.cc +++ b/common/node_bindings.cc @@ -7,7 +7,9 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" +#include "base/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "content/public/browser/browser_thread.h" #include "v8/include/v8.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" @@ -15,13 +17,29 @@ #include "vendor/node/src/node_internals.h" #include "vendor/node/src/node_javascript.h" +using content::BrowserThread; + namespace atom { +namespace { + +void UvNoOp(uv_async_t* handle, int status) { +} + +} // namespace + NodeBindings::NodeBindings(bool is_browser) - : is_browser_(is_browser) { + : is_browser_(is_browser), + message_loop_(NULL), + uv_loop_(uv_default_loop()), + embed_closed_(false) { } NodeBindings::~NodeBindings() { + // Clear uv. + embed_closed_ = true; + uv_thread_join(&embed_thread_); + uv_sem_destroy(&embed_sem_); } void NodeBindings::Initialize() { @@ -97,4 +115,78 @@ void NodeBindings::BindTo(WebKit::WebFrame* frame) { v8::Local::Cast(result)->Call(context->Global(), 2, args); } +void NodeBindings::PrepareMessageLoop() { + DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Add dummy handle for libuv, otherwise libuv would quit when there is + // nothing to do. + uv_async_init(uv_loop_, &dummy_uv_handle_, UvNoOp); + + // Start worker that will interrupt main loop when having uv events. + uv_sem_init(&embed_sem_, 0); + uv_thread_create(&embed_thread_, EmbedThreadRunner, this); +} + +void NodeBindings::RunMessageLoop() { + DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // The MessageLoop should have been created, remember the one in main thread. + message_loop_ = base::MessageLoop::current(); + + // Get notified when libuv's watcher queue changes. + uv_loop_->data = this; + uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged; + + // Run uv loop for once to give the uv__io_poll a chance to add all events. + UvRunOnce(); +} + +void NodeBindings::UvRunOnce() { + DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Enter node context while dealing with uv events. + v8::HandleScope scope; + v8::Context::Scope context_scope(node::g_context); + + // Deal with uv events. + int r = uv_run(uv_loop_, (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT)); + if (r == 0 || uv_loop_->stop_flag != 0) + message_loop_->QuitWhenIdle(); // Quit from uv. + + // Tell the worker thread to continue polling. + uv_sem_post(&embed_sem_); +} + +void NodeBindings::WakeupMainThread() { + DCHECK(message_loop_); + message_loop_->PostTask(FROM_HERE, base::Bind(&NodeBindings::UvRunOnce, + base::Unretained(this))); +} + +// static +void NodeBindings::EmbedThreadRunner(void *arg) { + NodeBindings* self = static_cast(arg); + + while (!self->embed_closed_) { + // Wait for the main loop to deal with events. + uv_sem_wait(&self->embed_sem_); + + self->PollEvents(); + + // Deal with event in main thread. + self->WakeupMainThread(); + } +} + +// static +void NodeBindings::OnWatcherQueueChanged(uv_loop_t* loop) { + NodeBindings* self = static_cast(loop->data); + + DCHECK(!self->is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // We need to break the io polling in the kqueue thread when loop's watcher + // queue changes, otherwise new events cannot be notified. + uv_async_send(&self->dummy_uv_handle_); +} + } // namespace atom diff --git a/common/node_bindings.h b/common/node_bindings.h index 887979c39135..78d0fe9f6504 100644 --- a/common/node_bindings.h +++ b/common/node_bindings.h @@ -6,11 +6,16 @@ #define RAVE_COMMON_NODE_BINDINGS_ #include "base/basictypes.h" +#include "vendor/node/deps/uv/include/uv.h" namespace WebKit { class WebFrame; } +namespace base { +class MessageLoop; +} + namespace atom { class NodeBindings { @@ -30,17 +35,51 @@ class NodeBindings { virtual void BindTo(WebKit::WebFrame* frame); // Prepare for message loop integration. - virtual void PrepareMessageLoop() = 0; + virtual void PrepareMessageLoop(); // Do message loop integration. - virtual void RunMessageLoop() = 0; + virtual void RunMessageLoop(); protected: explicit NodeBindings(bool is_browser); + // Called to poll events in new thread. + virtual void PollEvents() = 0; + + // Run the libuv loop for once. + void UvRunOnce(); + + // Make the main thread run libuv loop. + void WakeupMainThread(); + + // Are we running in browser. bool is_browser_; + // Main thread's MessageLoop. + base::MessageLoop* message_loop_; + + // Main thread's libuv loop. + uv_loop_t* uv_loop_; + + // Dummy handle to make uv's loop not quit. + uv_async_t dummy_uv_handle_; + + // Thread for polling events. + uv_thread_t embed_thread_; + + // Whether we're done. + bool embed_closed_; + + // Semaphore to wait for main loop in the embed thread. + uv_sem_t embed_sem_; + private: + // Thread to poll uv events. + static void EmbedThreadRunner(void *arg); + + // Called when uv's watcher queue changes. + static void OnWatcherQueueChanged(uv_loop_t* loop); + DISALLOW_COPY_AND_ASSIGN(NodeBindings); }; diff --git a/common/node_bindings_mac.cc b/common/node_bindings_mac.cc index 5db27a60d4f5..3fa70f0b61ee 100644 --- a/common/node_bindings_mac.cc +++ b/common/node_bindings_mac.cc @@ -4,131 +4,47 @@ #include "common/node_bindings_mac.h" +#include #include #include #include -#include "base/message_loop.h" -#include "content/public/browser/browser_thread.h" #include "vendor/node/src/node.h" #include "vendor/node/src/node_internals.h" -using content::BrowserThread; - namespace atom { -namespace { - -void UvNoOp(uv_async_t* handle, int status) { -} - -} - NodeBindingsMac::NodeBindingsMac(bool is_browser) : NodeBindings(is_browser), - message_loop_(NULL), - uv_loop_(uv_default_loop()), - kqueue_(kqueue()), - embed_closed_(false) { + kqueue_(kqueue()) { + // Add uv's backend fd to kqueue. + struct kevent ev; + EV_SET(&ev, uv_backend_fd(uv_loop_), EVFILT_READ, EV_ADD | EV_ENABLE, + 0, 0, 0); + kevent(kqueue_, &ev, 1, NULL, 0, NULL); } NodeBindingsMac::~NodeBindingsMac() { - // Clear uv. - embed_closed_ = true; - uv_thread_join(&embed_thread_); - uv_sem_destroy(&embed_sem_); } -void NodeBindingsMac::PrepareMessageLoop() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Add dummy handle for libuv, otherwise libuv would quit when there is - // nothing to do. - uv_async_init(uv_loop_, &dummy_uv_handle_, UvNoOp); - - // Start worker that will interrupt main loop when having uv events. - uv_sem_init(&embed_sem_, 0); - uv_thread_create(&embed_thread_, EmbedThreadRunner, this); -} - -void NodeBindingsMac::RunMessageLoop() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // The MessageLoop should have been created, remember the one in main thread. - message_loop_ = base::MessageLoop::current(); - - // Get notified when libuv's watcher queue changes. - uv_loop_->data = this; - uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged; - - // Run uv loop for once to give the uv__io_poll a chance to add all events. - UvRunOnce(); -} - -void NodeBindingsMac::UvRunOnce() { - DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // Enter node context while dealing with uv events. - v8::HandleScope scope; - v8::Context::Scope context_scope(node::g_context); - - // Deal with uv events. - int r = uv_run(uv_loop_, (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT)); - if (r == 0 || uv_loop_->stop_flag != 0) - message_loop_->QuitWhenIdle(); // Quit from uv. - - // Tell the worker thread to continue polling. - uv_sem_post(&embed_sem_); -} - -void NodeBindingsMac::WakeupMainThread() { - DCHECK(message_loop_); - message_loop_->PostTask(FROM_HERE, base::Bind(&NodeBindingsMac::UvRunOnce, - base::Unretained(this))); -} - -void NodeBindingsMac::EmbedThreadRunner(void *arg) { - NodeBindingsMac* self = static_cast(arg); - - uv_loop_t* loop = self->uv_loop_; - - // Add uv's backend fd to kqueue. - struct kevent ev; - EV_SET(&ev, uv_backend_fd(loop), EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); - kevent(self->kqueue_, &ev, 1, NULL, 0, NULL); - - while (!self->embed_closed_) { - // Wait for the main loop to deal with events. - uv_sem_wait(&self->embed_sem_); - - struct timespec spec; - int timeout = uv_backend_timeout(loop); - if (timeout != -1) { - spec.tv_sec = timeout / 1000; - spec.tv_nsec = (timeout % 1000) * 1000000; - } - - // Wait for new libuv events. - int r; - do { - r = ::kevent(self->kqueue_, NULL, 0, &ev, 1, - timeout == -1 ? NULL : &spec); - } while (r == -1 && errno == EINTR); - - // Deal with event in main thread. - self->WakeupMainThread(); +void NodeBindingsMac::PollEvents() { + struct timespec spec; + int timeout = uv_backend_timeout(uv_loop_); + if (timeout != -1) { + spec.tv_sec = timeout / 1000; + spec.tv_nsec = (timeout % 1000) * 1000000; } -} -// static -void NodeBindingsMac::OnWatcherQueueChanged(uv_loop_t* loop) { - NodeBindingsMac* self = static_cast(loop->data); + // Wait for new libuv events. + int r; + do { + struct kevent ev; + r = ::kevent(kqueue_, NULL, 0, &ev, 1, + timeout == -1 ? NULL : &spec); + } while (r == -1 && errno == EINTR); - DCHECK(!self->is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // We need to break the io polling in the kqueue thread when loop's watcher - // queue changes, otherwise new events cannot be notified. - uv_async_send(&self->dummy_uv_handle_); + // Deal with event in main thread. + WakeupMainThread(); } // static diff --git a/common/node_bindings_mac.h b/common/node_bindings_mac.h index 2e8c3e04e80c..6ae79c0a60b5 100644 --- a/common/node_bindings_mac.h +++ b/common/node_bindings_mac.h @@ -7,11 +7,6 @@ #include "base/compiler_specific.h" #include "common/node_bindings.h" -#include "vendor/node/deps/uv/include/uv.h" - -namespace base { -class MessageLoop; -} namespace atom { @@ -20,43 +15,12 @@ class NodeBindingsMac : public NodeBindings { explicit NodeBindingsMac(bool is_browser); virtual ~NodeBindingsMac(); - virtual void PrepareMessageLoop() OVERRIDE; - virtual void RunMessageLoop() OVERRIDE; - private: - // Run the libuv loop for once. - void UvRunOnce(); - - // Make the main thread run libuv loop. - void WakeupMainThread(); - - // Thread to poll uv events. - static void EmbedThreadRunner(void *arg); - - // Called when uv's watcher queue changes. - static void OnWatcherQueueChanged(uv_loop_t* loop); - - // Main thread's MessageLoop. - base::MessageLoop* message_loop_; - - // Main thread's libuv loop. - uv_loop_t* uv_loop_; + virtual void PollEvents() OVERRIDE; // Kqueue to poll for uv's backend fd. int kqueue_; - // Dummy handle to make uv's loop not quit. - uv_async_t dummy_uv_handle_; - - // Thread for polling events. - uv_thread_t embed_thread_; - - // Whether we're done. - bool embed_closed_; - - // Semaphore to wait for main loop in the embed thread. - uv_sem_t embed_sem_; - DISALLOW_COPY_AND_ASSIGN(NodeBindingsMac); };