Commit 0e0b9771 authored by jorlow@chromium.org's avatar jorlow@chromium.org

Simplify the WebKit thread model. It's now created/destroyed on the UI thread...

Simplify the WebKit thread model.  It's now created/destroyed on the UI thread (before/after the IO thread is started/stopped).  The WebKit thread is created lazily as needed (while on the IO thread).TEST=noneBUG=none

Review URL: http://codereview.chromium.org/149238

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20109 0039d316-1c4b-4281-b951-d872f2087c98
parent e9035537
...@@ -9,6 +9,7 @@ static const char* chrome_thread_names[ChromeThread::ID_COUNT] = { ...@@ -9,6 +9,7 @@ static const char* chrome_thread_names[ChromeThread::ID_COUNT] = {
"Chrome_IOThread", // IO "Chrome_IOThread", // IO
"Chrome_FileThread", // FILE "Chrome_FileThread", // FILE
"Chrome_DBThread", // DB "Chrome_DBThread", // DB
"Chrome_WebKitThread", // WEBKIT
"Chrome_HistoryThread", // HISTORY "Chrome_HistoryThread", // HISTORY
#if defined(OS_LINUX) #if defined(OS_LINUX)
"Chrome_Background_X11Thread", // BACKGROUND_X11 "Chrome_Background_X11Thread", // BACKGROUND_X11
...@@ -21,6 +22,7 @@ ChromeThread* ChromeThread::chrome_threads_[ID_COUNT] = { ...@@ -21,6 +22,7 @@ ChromeThread* ChromeThread::chrome_threads_[ID_COUNT] = {
NULL, // IO NULL, // IO
NULL, // FILE NULL, // FILE
NULL, // DB NULL, // DB
NULL, // WEBKIT
NULL, // HISTORY NULL, // HISTORY
#if defined(OS_LINUX) #if defined(OS_LINUX)
NULL, // BACKGROUND_X11 NULL, // BACKGROUND_X11
...@@ -51,3 +53,13 @@ MessageLoop* ChromeThread::GetMessageLoop(ID identifier) { ...@@ -51,3 +53,13 @@ MessageLoop* ChromeThread::GetMessageLoop(ID identifier) {
return NULL; return NULL;
} }
// static
bool ChromeThread::CurrentlyOn(ID identifier) {
// MessageLoop::current() will return NULL if none is running. This is often
// true when running under unit tests. This behavior actually works out
// pretty convienently (as is mentioned in the header file comment), but it's
// worth noting here.
MessageLoop* message_loop = GetMessageLoop(identifier);
return MessageLoop::current() == message_loop;
}
...@@ -38,6 +38,10 @@ class ChromeThread : public base::Thread { ...@@ -38,6 +38,10 @@ class ChromeThread : public base::Thread {
// This is the thread that interacts with the database. // This is the thread that interacts with the database.
DB, DB,
// This is the "main" thread for WebKit within the browser process when
// NOT in --single-process mode.
WEBKIT,
// This is the thread that interacts with the history database. // This is the thread that interacts with the history database.
HISTORY, HISTORY,
...@@ -68,6 +72,16 @@ class ChromeThread : public base::Thread { ...@@ -68,6 +72,16 @@ class ChromeThread : public base::Thread {
// //
static MessageLoop* GetMessageLoop(ID identifier); static MessageLoop* GetMessageLoop(ID identifier);
// Callable on any thread. Returns whether you're currently on a particular
// thread.
//
// WARNING:
// When running under unit-tests, this will return true if you're on the
// main thread and the thread ID you pass in isn't running. This is
// normally the correct behavior because you want to ignore these asserts
// unless you've specifically spun up the threads, but be mindful of it.
static bool CurrentlyOn(ID identifier);
private: private:
// The identifier of this thread. Only one thread can exist with a given // The identifier of this thread. Only one thread can exist with a given
// identifier at a given time. // identifier at a given time.
......
...@@ -16,17 +16,12 @@ DOMStorageDispatcherHost::DOMStorageDispatcherHost( ...@@ -16,17 +16,12 @@ DOMStorageDispatcherHost::DOMStorageDispatcherHost(
webkit_thread_(webkit_thread), webkit_thread_(webkit_thread),
message_sender_(message_sender) { message_sender_(message_sender) {
DCHECK(webkit_context_.get()); DCHECK(webkit_context_.get());
DCHECK(webkit_thread_.get()); DCHECK(webkit_thread_);
DCHECK(message_sender_); DCHECK(message_sender_);
} }
DOMStorageDispatcherHost::~DOMStorageDispatcherHost() { DOMStorageDispatcherHost::~DOMStorageDispatcherHost() {
DCHECK(!message_sender_); DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
}
void DOMStorageDispatcherHost::Shutdown() {
DCHECK(IsOnIOThread());
AutoLock lock(message_sender_lock_);
message_sender_ = NULL; message_sender_ = NULL;
} }
...@@ -37,34 +32,21 @@ bool DOMStorageDispatcherHost::OnMessageReceived(const IPC::Message& msg) { ...@@ -37,34 +32,21 @@ bool DOMStorageDispatcherHost::OnMessageReceived(const IPC::Message& msg) {
} }
void DOMStorageDispatcherHost::Send(IPC::Message* message) { void DOMStorageDispatcherHost::Send(IPC::Message* message) {
if (IsOnIOThread()) {
if (message_sender_)
message_sender_->Send(message);
else
delete message;
}
// If message_sender_ is NULL, the IO thread has either gone away
// or will do so soon. By holding this lock until we finish posting to the
// thread, we block the IO thread from completely shutting down benieth us.
AutoLock lock(message_sender_lock_);
if (!message_sender_) { if (!message_sender_) {
delete message; delete message;
return; return;
} }
if (ChromeThread::CurrentlyOn(ChromeThread::IO)) {
message_sender_->Send(message);
return;
}
// The IO thread can't dissapear while the WebKit thread is still running.
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::WEBKIT));
MessageLoop* io_loop = ChromeThread::GetMessageLoop(ChromeThread::IO); MessageLoop* io_loop = ChromeThread::GetMessageLoop(ChromeThread::IO);
CancelableTask* task = NewRunnableMethod(this, CancelableTask* task = NewRunnableMethod(this,
&DOMStorageDispatcherHost::Send, &DOMStorageDispatcherHost::Send,
message); message);
io_loop->PostTask(FROM_HERE, task); io_loop->PostTask(FROM_HERE, task);
} }
bool DOMStorageDispatcherHost::IsOnIOThread() const {
MessageLoop* io_loop = ChromeThread::GetMessageLoop(ChromeThread::IO);
return MessageLoop::current() == io_loop;
}
bool DOMStorageDispatcherHost::IsOnWebKitThread() const {
return MessageLoop::current() == webkit_thread_->GetMessageLoop();
}
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define CHROME_BROWSER_IN_PROCESS_WEBKIT_DOM_STORAGE_DISPATCHER_HOST_H_ #define CHROME_BROWSER_IN_PROCESS_WEBKIT_DOM_STORAGE_DISPATCHER_HOST_H_
#include "base/ref_counted.h" #include "base/ref_counted.h"
#include "base/thread.h"
#include "chrome/common/ipc_message.h" #include "chrome/common/ipc_message.h"
class WebKitContext; class WebKitContext;
...@@ -15,7 +14,7 @@ class WebKitThread; ...@@ -15,7 +14,7 @@ class WebKitThread;
// This class handles the logistics of DOM Storage within the browser process. // This class handles the logistics of DOM Storage within the browser process.
// It mostly ferries information between IPCs and the WebKit implementations, // It mostly ferries information between IPCs and the WebKit implementations,
// but it also handles some special cases like when renderer processes die. // but it also handles some special cases like when renderer processes die.
// THIS CLASS MUST NOT BE DESTROYED ON THE WEBKIT THREAD (for now). // THIS CLASS MUST NOT BE DESTROYED ON THE WEBKIT THREAD.
class DOMStorageDispatcherHost : class DOMStorageDispatcherHost :
public base::RefCountedThreadSafe<DOMStorageDispatcherHost> { public base::RefCountedThreadSafe<DOMStorageDispatcherHost> {
public: public:
...@@ -23,10 +22,6 @@ class DOMStorageDispatcherHost : ...@@ -23,10 +22,6 @@ class DOMStorageDispatcherHost :
DOMStorageDispatcherHost(IPC::Message::Sender* message_sender, DOMStorageDispatcherHost(IPC::Message::Sender* message_sender,
WebKitContext*, WebKitThread*); WebKitContext*, WebKitThread*);
// Only call Shutdown from the IO thread. Shutdown warns us that we're going
// to go away soon and tells us not to send anything else to the IO thread.
void Shutdown();
// Only call from IO thread. // Only call from IO thread.
bool OnMessageReceived(const IPC::Message& message); bool OnMessageReceived(const IPC::Message& message);
...@@ -38,18 +33,13 @@ class DOMStorageDispatcherHost : ...@@ -38,18 +33,13 @@ class DOMStorageDispatcherHost :
friend class base::RefCountedThreadSafe<DOMStorageDispatcherHost>; friend class base::RefCountedThreadSafe<DOMStorageDispatcherHost>;
~DOMStorageDispatcherHost(); ~DOMStorageDispatcherHost();
// Obviously can be called from any thread. // Data shared between renderer processes with the same profile.
bool IsOnIOThread() const;
bool IsOnWebKitThread() const;
// Are immutable and are always valid throughout the lifetime of the object.
scoped_refptr<WebKitContext> webkit_context_; scoped_refptr<WebKitContext> webkit_context_;
scoped_refptr<WebKitThread> webkit_thread_;
// We keep the message_sender_ pointer for sending messages. All access // ResourceDispatcherHost takes care of destruction. Immutable.
// to the message_sender_ (and the IO thread in general) should be done under WebKitThread* webkit_thread_;
// this lock and only if message_sender_ is non-NULL.
Lock message_sender_lock_; // Only set on the IO thread.
IPC::Message::Sender* message_sender_; IPC::Message::Sender* message_sender_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DOMStorageDispatcherHost); DISALLOW_IMPLICIT_CONSTRUCTORS(DOMStorageDispatcherHost);
......
...@@ -4,21 +4,23 @@ ...@@ -4,21 +4,23 @@
#include "chrome/browser/in_process_webkit/webkit_thread.h" #include "chrome/browser/in_process_webkit/webkit_thread.h"
#include "base/command_line.h"
#include "chrome/browser/in_process_webkit/browser_webkitclient_impl.h" #include "chrome/browser/in_process_webkit/browser_webkitclient_impl.h"
#include "chrome/common/chrome_switches.h"
#include "webkit/api/public/WebKit.h" #include "webkit/api/public/WebKit.h"
base::LazyInstance<Lock> WebKitThread::global_webkit_lock_( // This happens on the UI thread before the IO thread has been shut down.
base::LINKER_INITIALIZED); WebKitThread::WebKitThread() {
int WebKitThread::global_webkit_ref_count_ = 0; // The thread is started lazily by InitializeThread() on the IO thread.
WebKitThread::InternalWebKitThread* WebKitThread::global_webkit_thread_ = NULL; }
WebKitThread::WebKitThread() // This happens on the UI thread after the IO thread has been shut down.
: cached_webkit_thread_(NULL) { WebKitThread::~WebKitThread() {
// The thread is started lazily by InitializeThread(). DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::WEBKIT));
} }
WebKitThread::InternalWebKitThread::InternalWebKitThread() WebKitThread::InternalWebKitThread::InternalWebKitThread()
: base::Thread("WebKit"), : ChromeThread(ChromeThread::WEBKIT),
webkit_client_(NULL) { webkit_client_(NULL) {
} }
...@@ -28,42 +30,24 @@ void WebKitThread::InternalWebKitThread::Init() { ...@@ -28,42 +30,24 @@ void WebKitThread::InternalWebKitThread::Init() {
DCHECK(webkit_client_); DCHECK(webkit_client_);
WebKit::initialize(webkit_client_); WebKit::initialize(webkit_client_);
// Don't do anything heavyweight here since this can block the IO thread from // Don't do anything heavyweight here since this can block the IO thread from
// executing (since InitializeThread() is often called on the IO thread). // executing (since InitializeThread() is called on the IO thread).
} }
void WebKitThread::InternalWebKitThread::CleanUp() { void WebKitThread::InternalWebKitThread::CleanUp() {
// Don't do anything heavyweight here since this can block the IO thread from
// executing (since the thread is shutdown from the IO thread).
DCHECK(webkit_client_); DCHECK(webkit_client_);
WebKit::shutdown(); WebKit::shutdown();
delete webkit_client_; delete webkit_client_;
webkit_client_ = NULL;
} }
WebKitThread::~WebKitThread() { MessageLoop* WebKitThread::InitializeThread() {
AutoLock lock(global_webkit_lock_.Get()); if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
if (cached_webkit_thread_) { return NULL;
DCHECK(global_webkit_ref_count_ > 0);
if (--global_webkit_ref_count_ == 0) {
// TODO(jorlow): Make this safe.
DCHECK(MessageLoop::current() != global_webkit_thread_->message_loop());
global_webkit_thread_->Stop();
delete global_webkit_thread_;
global_webkit_thread_ = NULL;
}
}
}
void WebKitThread::InitializeThread() { DCHECK(!webkit_thread_.get());
AutoLock lock(global_webkit_lock_.Get()); webkit_thread_.reset(new InternalWebKitThread);
if (!cached_webkit_thread_) { bool started = webkit_thread_->Start();
if (!global_webkit_thread_) { DCHECK(started);
global_webkit_thread_ = new InternalWebKitThread; return webkit_thread_->message_loop();
DCHECK(global_webkit_thread_);
bool started = global_webkit_thread_->Start();
DCHECK(started);
}
++global_webkit_ref_count_;
// The cached version can be accessed outside of global_webkit_lock_.
cached_webkit_thread_ = global_webkit_thread_;
}
DCHECK(cached_webkit_thread_->IsRunning());
} }
...@@ -10,36 +10,34 @@ ...@@ -10,36 +10,34 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/ref_counted.h" #include "base/ref_counted.h"
#include "base/thread.h" #include "base/thread.h"
#include "chrome/browser/chrome_thread.h"
class BrowserWebKitClientImpl; class BrowserWebKitClientImpl;
// This is an object that represents WebKit's "main" thread within the browser // This is an object that represents WebKit's "main" thread within the browser
// process. You can create as many instances of this class as you'd like; // process. It should be instantiated and destroyed on the UI thread
// they'll all point to the same thread and you're guaranteed they'll // before/after the IO thread is created/destroyed. All other usage should be
// initialize in a thread-safe way, though WebKitThread instances should // on the IO thread. If the browser is being run in --single-process mode, a
// probably be shared when it's easy to do so. The first time you call // thread will never be spun up, and GetMessageLoop() will always return NULL.
// GetMessageLoop() or EnsureWebKitInitialized() the thread will be created class WebKitThread {
// and WebKit initialized. When the last instance of WebKitThread is
// destroyed, WebKit is shut down and the thread is stopped.
// THIS CLASS MUST NOT BE DEREFED TO 0 ON THE WEBKIT THREAD (for now).
class WebKitThread : public base::RefCountedThreadSafe<WebKitThread> {
public: public:
// Called from the UI thread.
WebKitThread(); WebKitThread();
~WebKitThread();
// Returns the message loop for the WebKit thread unless we're in
// --single-processuntil mode, in which case it'll return NULL. Only call
// from the IO thread. Only do fast-path work here.
MessageLoop* GetMessageLoop() { MessageLoop* GetMessageLoop() {
if (!cached_webkit_thread_) DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
InitializeThread(); if (!webkit_thread_.get())
return cached_webkit_thread_->message_loop(); return InitializeThread();
} return webkit_thread_->message_loop();
void EnsureWebKitInitialized() {
if (!cached_webkit_thread_)
InitializeThread();
} }
private: private:
// Must be private so that we can carefully control its lifetime. // Must be private so that we can carefully control its lifetime.
class InternalWebKitThread : public base::Thread { class InternalWebKitThread : public ChromeThread {
public: public:
InternalWebKitThread(); InternalWebKitThread();
virtual ~InternalWebKitThread() { } virtual ~InternalWebKitThread() { }
...@@ -52,22 +50,13 @@ class WebKitThread : public base::RefCountedThreadSafe<WebKitThread> { ...@@ -52,22 +50,13 @@ class WebKitThread : public base::RefCountedThreadSafe<WebKitThread> {
BrowserWebKitClientImpl* webkit_client_; BrowserWebKitClientImpl* webkit_client_;
}; };
friend class base::RefCountedThreadSafe<WebKitThread>; // Returns the WebKit thread's message loop or NULL if we're in
~WebKitThread(); // --single-process mode. Do slow-path initialization work here.
MessageLoop* InitializeThread();
void InitializeThread();
// If this is set, then this object has incremented the global WebKit ref
// count and will shutdown the thread if it sees the ref count go to 0.
// It's assumed that once this is non-NULL, the pointer will be valid until
// destruction.
InternalWebKitThread* cached_webkit_thread_;
// If there are multiple WebKitThread object (should only be possible in // Pointer to the actual WebKitThread. NULL if not yet started. Only modify
// unittests at the moment), make sure they all share one real thread. // from the IO thread while the WebKit thread is not running.
static base::LazyInstance<Lock> global_webkit_lock_; scoped_ptr<InternalWebKitThread> webkit_thread_;
static int global_webkit_ref_count_;
static InternalWebKitThread* global_webkit_thread_;
DISALLOW_COPY_AND_ASSIGN(WebKitThread); DISALLOW_COPY_AND_ASSIGN(WebKitThread);
}; };
......
...@@ -5,13 +5,4 @@ ...@@ -5,13 +5,4 @@
#include "chrome/browser/in_process_webkit/webkit_thread.h" #include "chrome/browser/in_process_webkit/webkit_thread.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
// This is important because if there are 2 different message loops, we must // TODO(jorlow): Write some tests. http://crbug.com/16155
// have 2 different WebKit threads which would be very bad.
TEST(WebKitThreadTest, TwoThreadsShareMessageLoopTest) {
scoped_refptr<WebKitThread> thread_a = new WebKitThread;
scoped_refptr<WebKitThread> thread_b = new WebKitThread;
MessageLoop* loop_a = thread_a->GetMessageLoop();
MessageLoop* loop_b = thread_b->GetMessageLoop();
ASSERT_FALSE(loop_a == NULL);
ASSERT_EQ(loop_a, loop_b);
}
...@@ -292,7 +292,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { ...@@ -292,7 +292,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
} }
WebKitThread* webkit_thread() const { WebKitThread* webkit_thread() const {
return webkit_thread_; return webkit_thread_.get();
} }
MessageLoop* ui_loop() const { return ui_loop_; } MessageLoop* ui_loop() const { return ui_loop_; }
...@@ -529,7 +529,8 @@ class ResourceDispatcherHost : public URLRequest::Delegate { ...@@ -529,7 +529,8 @@ class ResourceDispatcherHost : public URLRequest::Delegate {
scoped_refptr<SafeBrowsingService> safe_browsing_; scoped_refptr<SafeBrowsingService> safe_browsing_;
scoped_refptr<WebKitThread> webkit_thread_; // We own the WebKit thread and see to its destruction.
scoped_ptr<WebKitThread> webkit_thread_;
// Request ID for browser initiated requests. request_ids generated by // Request ID for browser initiated requests. request_ids generated by
// child processes are counted up from 0, while browser created requests // child processes are counted up from 0, while browser created requests
......
...@@ -175,10 +175,7 @@ ResourceMessageFilter::ResourceMessageFilter( ...@@ -175,10 +175,7 @@ ResourceMessageFilter::ResourceMessageFilter(
ResourceMessageFilter::~ResourceMessageFilter() { ResourceMessageFilter::~ResourceMessageFilter() {
// This function should be called on the IO thread. // This function should be called on the IO thread.
DCHECK(MessageLoop::current() == DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
ChromeThread::GetMessageLoop(ChromeThread::IO));
dom_storage_dispatcher_host_->Shutdown();
// Let interested observers know we are being deleted. // Let interested observers know we are being deleted.
NotificationService::current()->Notify( NotificationService::current()->Notify(
...@@ -452,8 +449,7 @@ void ResourceMessageFilter::OnGetDataDir(std::wstring* data_dir) { ...@@ -452,8 +449,7 @@ void ResourceMessageFilter::OnGetDataDir(std::wstring* data_dir) {
void ResourceMessageFilter::OnPluginMessage(const FilePath& plugin_path, void ResourceMessageFilter::OnPluginMessage(const FilePath& plugin_path,
const std::vector<uint8>& data) { const std::vector<uint8>& data) {
DCHECK(MessageLoop::current() == DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
ChromeThread::GetMessageLoop(ChromeThread::IO));
ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path); ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path);
if (chrome_plugin) { if (chrome_plugin) {
...@@ -466,8 +462,7 @@ void ResourceMessageFilter::OnPluginMessage(const FilePath& plugin_path, ...@@ -466,8 +462,7 @@ void ResourceMessageFilter::OnPluginMessage(const FilePath& plugin_path,
void ResourceMessageFilter::OnPluginSyncMessage(const FilePath& plugin_path, void ResourceMessageFilter::OnPluginSyncMessage(const FilePath& plugin_path,
const std::vector<uint8>& data, const std::vector<uint8>& data,
std::vector<uint8> *retval) { std::vector<uint8> *retval) {
DCHECK(MessageLoop::current() == DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
ChromeThread::GetMessageLoop(ChromeThread::IO));
ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path); ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path);
if (chrome_plugin) { if (chrome_plugin) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment