Integrate libuv with browser's message loop.
This commit is contained in:
parent
40307126d7
commit
409228207c
6 changed files with 272 additions and 5 deletions
2
atom.gyp
2
atom.gyp
|
@ -20,6 +20,8 @@
|
|||
'browser/native_window_mac.mm',
|
||||
'common/node_bindings.cc',
|
||||
'common/node_bindings.h',
|
||||
'common/node_bindings_mac.h',
|
||||
'common/node_bindings_mac.mm',
|
||||
'common/options_switches.cc',
|
||||
'common/options_switches.h',
|
||||
'renderer/atom_render_view_observer.cc',
|
||||
|
|
|
@ -15,19 +15,29 @@
|
|||
namespace atom {
|
||||
|
||||
AtomBrowserMainParts::AtomBrowserMainParts()
|
||||
: node_bindings_(new NodeBindings(true)) {
|
||||
: node_bindings_(NodeBindings::Create(true)) {
|
||||
}
|
||||
|
||||
AtomBrowserMainParts::~AtomBrowserMainParts() {
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||
brightray::BrowserMainParts::PostEarlyInitialization();
|
||||
|
||||
node_bindings_->Initialize();
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PreMainMessageLoopStart() {
|
||||
brightray::BrowserMainParts::PreMainMessageLoopStart();
|
||||
|
||||
node_bindings_->PrepareMessageLoop();
|
||||
}
|
||||
|
||||
void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
||||
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue);
|
||||
options->SetInteger("width", 800);
|
||||
options->SetInteger("height", 600);
|
||||
|
@ -38,7 +48,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
|||
window->InitFromOptions(options.get());
|
||||
|
||||
window->GetWebContents()->GetController().LoadURL(
|
||||
GURL("http://adam.roben.org/brightray_example/start.html"),
|
||||
GURL("http://localhost"),
|
||||
content::Referrer(),
|
||||
content::PAGE_TRANSITION_AUTO_TOPLEVEL,
|
||||
std::string());
|
||||
|
|
|
@ -19,6 +19,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
|||
|
||||
protected:
|
||||
virtual void PostEarlyInitialization() OVERRIDE;
|
||||
virtual void PreMainMessageLoopStart() OVERRIDE;
|
||||
virtual void PreMainMessageLoopRun() OVERRIDE;
|
||||
|
||||
private:
|
||||
|
|
|
@ -11,13 +11,22 @@ namespace atom {
|
|||
|
||||
class NodeBindings {
|
||||
public:
|
||||
NodeBindings(bool is_browser);
|
||||
static NodeBindings* Create(bool is_browser);
|
||||
|
||||
virtual ~NodeBindings();
|
||||
|
||||
// Setup everything including V8, libuv and node.js main script.
|
||||
void Initialize();
|
||||
virtual void Initialize();
|
||||
|
||||
// Prepare for message loop integration.
|
||||
virtual void PrepareMessageLoop() = 0;
|
||||
|
||||
// Do message loop integration.
|
||||
virtual void RunMessageLoop() = 0;
|
||||
|
||||
protected:
|
||||
explicit NodeBindings(bool is_browser);
|
||||
|
||||
private:
|
||||
bool is_browser_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeBindings);
|
||||
|
|
66
common/node_bindings_mac.h
Normal file
66
common/node_bindings_mac.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#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"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NodeBindingsMac : public NodeBindings {
|
||||
public:
|
||||
explicit NodeBindingsMac(bool is_browser);
|
||||
virtual ~NodeBindingsMac();
|
||||
|
||||
virtual void PrepareMessageLoop() OVERRIDE;
|
||||
virtual void RunMessageLoop() OVERRIDE;
|
||||
|
||||
private:
|
||||
// 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);
|
||||
|
||||
// Called when uv's watcher queue changes.
|
||||
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
||||
|
||||
// Main thread's loop.
|
||||
uv_loop_t* 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_;
|
||||
|
||||
// Captured oneshot event in embed thread.
|
||||
bool has_pending_event_;
|
||||
struct ::kevent event_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeBindingsMac);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_NODE_BINDINGS_MAC_
|
179
common/node_bindings_mac.mm
Normal file
179
common/node_bindings_mac.mm
Normal file
|
@ -0,0 +1,179 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "node_bindings_mac.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 {
|
||||
|
||||
namespace {
|
||||
|
||||
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) {
|
||||
}
|
||||
|
||||
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(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));
|
||||
|
||||
loop_->data = this;
|
||||
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::OnKqueueHasNewEvents() {
|
||||
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
DealWithPendingEvent();
|
||||
UvRunOnce();
|
||||
}
|
||||
|
||||
void NodeBindingsMac::UvRunOnce() {
|
||||
DCHECK(!is_browser_ || BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
// Enter node context while dealing with uv events.
|
||||
v8::Context::Scope context_scope(node::g_context);
|
||||
|
||||
// Deal with uv events.
|
||||
int r = uv_run(loop_, (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT));
|
||||
if (r == 0 || loop_->stop_flag != 0)
|
||||
MessageLoop::current()->QuitWhenIdle(); // Quit from uv.
|
||||
|
||||
// Tell the worker thread to continue polling.
|
||||
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);
|
||||
|
||||
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) {
|
||||
spec.tv_sec = timeout / 1000;
|
||||
spec.tv_nsec = (timeout % 1000) * 1000000;
|
||||
}
|
||||
|
||||
// Wait for new libuv events.
|
||||
int r;
|
||||
do {
|
||||
r = ::kevent(uv_backend_fd(loop), NULL, 0, &self->event_, 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void NodeBindingsMac::OnWatcherQueueChanged(uv_loop_t* loop) {
|
||||
NodeBindingsMac* self = static_cast<NodeBindingsMac*>(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_);
|
||||
}
|
||||
|
||||
// static
|
||||
NodeBindings* NodeBindings::Create(bool is_browser) {
|
||||
return new NodeBindingsMac(is_browser);
|
||||
}
|
||||
|
||||
} // namespace atom
|
Loading…
Add table
Reference in a new issue