Improve the way to integrate libuv.

Now we poll libuv's events in embed thread by adding main loop's backend
fd to a new kqueue fd, instead of directly polling on main loop's
backend fd. In this way we can avoid dealing with one shot events
overselves.
This commit is contained in:
Cheng Zhao 2013-04-19 21:21:04 +08:00
parent 603ae8b62e
commit 855b42551d
2 changed files with 18 additions and 79 deletions

View file

@ -5,10 +5,6 @@
#ifndef ATOM_COMMON_NODE_BINDINGS_MAC_
#define ATOM_COMMON_NODE_BINDINGS_MAC_
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include "base/compiler_specific.h"
#include "common/node_bindings.h"
#include "vendor/node/deps/uv/include/uv.h"
@ -27,12 +23,6 @@ class NodeBindingsMac : public NodeBindings {
// Run the libuv loop for once.
void UvRunOnce();
// Run pending one shot events if we have.
void DealWithPendingEvent();
// Called when kqueue notifies new event.
void OnKqueueHasNewEvents();
// Thread to poll uv events.
static void EmbedThreadRunner(void *arg);
@ -42,6 +32,9 @@ class NodeBindingsMac : public NodeBindings {
// Main thread's loop.
uv_loop_t* loop_;
// 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_;
@ -54,10 +47,6 @@ class NodeBindingsMac : public NodeBindings {
// Semaphore to wait for main loop in the embed thread.
uv_sem_t embed_sem_;
// Captured oneshot event in embed thread.
bool has_pending_event_;
struct ::kevent event_;
DISALLOW_COPY_AND_ASSIGN(NodeBindingsMac);
};

View file

@ -4,15 +4,15 @@
#include "common/node_bindings_mac.h"
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
#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"
#define UV__POLLIN 1
#define UV__POLLOUT 2
#define UV__POLLERR 4
using content::BrowserThread;
namespace atom {
@ -27,8 +27,8 @@ void UvNoOp(uv_async_t* handle, int status) {
NodeBindingsMac::NodeBindingsMac(bool is_browser)
: NodeBindings(is_browser),
loop_(uv_default_loop()),
embed_closed_(false),
has_pending_event_(false) {
kqueue_(kqueue()),
embed_closed_(false) {
}
NodeBindingsMac::~NodeBindingsMac() {
@ -60,13 +60,6 @@ void NodeBindingsMac::RunMessageLoop() {
UvRunOnce();
}
void NodeBindingsMac::OnKqueueHasNewEvents() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
DealWithPendingEvent();
UvRunOnce();
}
void NodeBindingsMac::UvRunOnce() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
@ -82,58 +75,20 @@ void NodeBindingsMac::UvRunOnce() {
uv_sem_post(&embed_sem_);
}
void NodeBindingsMac::DealWithPendingEvent() {
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
if (has_pending_event_) {
has_pending_event_ = false;
// Enter node context while dealing with uv events.
v8::Context::Scope context_scope(node::g_context);
// The pending event must be a one shot event, so we don't need to
// delete it from kqueue.
uv__io_t* w = loop_->watchers[event_.ident];
if (w) {
unsigned int revents;
if (event_.filter == EVFILT_VNODE) {
w->cb(loop_, w, event_.fflags);
return;
}
revents = 0;
if (event_.filter == EVFILT_READ && (w->events & UV__POLLIN)) {
revents |= UV__POLLIN;
w->rcount = event_.data;
}
if (event_.filter == EVFILT_WRITE && (w->events & UV__POLLOUT)) {
revents |= UV__POLLOUT;
w->wcount = event_.data;
}
if (event_.flags & EV_ERROR)
revents |= UV__POLLERR;
if (revents == 0)
return;
w->cb(loop_, w, revents);
}
}
}
void NodeBindingsMac::EmbedThreadRunner(void *arg) {
NodeBindingsMac* self = static_cast<NodeBindingsMac*>(arg);
uv_loop_t* loop = self->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_);
uv_loop_t* loop = self->loop_;
struct timespec spec;
int timeout = uv_backend_timeout(loop);
if (timeout != -1) {
@ -144,18 +99,13 @@ void NodeBindingsMac::EmbedThreadRunner(void *arg) {
// Wait for new libuv events.
int r;
do {
r = ::kevent(uv_backend_fd(loop), NULL, 0, &self->event_, 1,
r = ::kevent(self->kqueue_, NULL, 0, &ev, 1,
timeout == -1 ? NULL : &spec);
} while (r == -1 && errno == EINTR);
// We captured a one shot event, we should deal it in main thread because
// next kevent call will not catch it.
if (r == 1 && (self->event_.flags & EV_ONESHOT))
self->has_pending_event_ = true;
// Deal with event in main thread.
dispatch_async(dispatch_get_main_queue(), ^{
self->OnKqueueHasNewEvents();
self->UvRunOnce();
});
}
}