diff options
author | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-28 15:56:41 +0000 |
---|---|---|
committer | joi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-28 15:56:41 +0000 |
commit | 2e5b60a280076017d700e56a5cb81ec62149a9bc (patch) | |
tree | e1f212d0f3cbdb7c04f4fd8fc67e1fcd26c350da /content/browser | |
parent | e04be707101e60bb5512c69a64ce5b64a1c4eeba (diff) | |
download | chromium_src-2e5b60a280076017d700e56a5cb81ec62149a9bc.zip chromium_src-2e5b60a280076017d700e56a5cb81ec62149a9bc.tar.gz chromium_src-2e5b60a280076017d700e56a5cb81ec62149a9bc.tar.bz2 |
Have content/ create and destroy its own threads. (Re-land)
Change embedding API and embedders to allow for this.
Push inheritance of base::Thread down to content::BrowserThreadImpl so
that content::BrowserThread is just a namespace for API functions.
This change temporarily disables chrome_frame_net_tests as agreed by the CF lead, see bug 105435.
TBR=ben@chromium.org (IWYU change only)
BUG=98716,104578,105435
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=111695
Reverted (problems on official bot): r111698
Review URL: http://codereview.chromium.org/8477004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111705 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser')
-rw-r--r-- | content/browser/browser_main_loop.cc | 199 | ||||
-rw-r--r-- | content/browser/browser_main_loop.h | 17 | ||||
-rw-r--r-- | content/browser/browser_process_sub_thread.cc | 6 | ||||
-rw-r--r-- | content/browser/browser_thread_impl.cc | 160 | ||||
-rw-r--r-- | content/browser/browser_thread_impl.h | 28 |
5 files changed, 333 insertions, 77 deletions
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index dd93e58..c6d448a 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc @@ -16,6 +16,7 @@ #include "content/common/hi_res_timer_manager.h" #include "content/common/sandbox_policy.h" #include "content/public/browser/browser_main_parts.h" +#include "content/public/browser/browser_shutdown.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" @@ -145,6 +146,33 @@ static void SetUpGLibLogHandler() { namespace content { +// The currently-running BrowserMainLoop. There can be one or zero. +// This is stored to enable immediate shutdown when needed. +BrowserMainLoop* current_browser_main_loop = NULL; + +// This is just to be able to keep ShutdownThreadsAndCleanUp out of +// the public interface of BrowserMainLoop. +class BrowserShutdownImpl { + public: + static void ImmediateShutdownAndExitProcess() { + DCHECK(current_browser_main_loop); + current_browser_main_loop->ShutdownThreadsAndCleanUp(); + +#if defined(OS_WIN) + // At this point the message loop is still running yet we've shut everything + // down. If any messages are processed we'll likely crash. Exit now. + ExitProcess(content::RESULT_CODE_NORMAL_EXIT); +#elif defined(OS_POSIX) && !defined(OS_MACOSX) + _exit(content::RESULT_CODE_NORMAL_EXIT); +#else + NOTIMPLEMENTED(); +#endif + } +}; + +void ImmediateShutdownAndExitProcess() { + BrowserShutdownImpl::ImmediateShutdownAndExitProcess(); +} // BrowserMainLoop construction / destructione ============================= @@ -152,12 +180,16 @@ BrowserMainLoop::BrowserMainLoop(const content::MainFunctionParams& parameters) : parameters_(parameters), parsed_command_line_(parameters.command_line), result_code_(content::RESULT_CODE_NORMAL_EXIT) { + DCHECK(!current_browser_main_loop); + current_browser_main_loop = this; #if defined(OS_WIN) OleInitialize(NULL); #endif } BrowserMainLoop::~BrowserMainLoop() { + DCHECK_EQ(this, current_browser_main_loop); + current_browser_main_loop = NULL; #if defined(OS_WIN) OleUninitialize(); #endif @@ -262,6 +294,85 @@ void BrowserMainLoop::MainMessageLoopStart() { void BrowserMainLoop::RunMainMessageLoopParts( bool* completed_main_message_loop) { if (parts_.get()) + parts_->PreCreateThreads(); + + base::Thread::Options default_options; + base::Thread::Options io_message_loop_options; + io_message_loop_options.message_loop_type = MessageLoop::TYPE_IO; + base::Thread::Options ui_message_loop_options; + ui_message_loop_options.message_loop_type = MessageLoop::TYPE_UI; + + // Start threads in the order they occur in the BrowserThread::ID + // enumeration, except for BrowserThread::UI which is the main + // thread. + // + // Must be size_t so we can increment it. + for (size_t thread_id = BrowserThread::UI + 1; + thread_id < BrowserThread::ID_COUNT; + ++thread_id) { + scoped_ptr<BrowserProcessSubThread>* thread_to_start = NULL; + base::Thread::Options* options = &default_options; + + switch (thread_id) { + case BrowserThread::DB: + thread_to_start = &db_thread_; + break; + case BrowserThread::WEBKIT: + // For now, the WebKit thread in the browser is owned by + // ResourceDispatcherHost, not by the content framework. Until + // this is fixed, we don't start the thread but still call + // Pre/PostStartThread for the ID. + break; + case BrowserThread::FILE: + thread_to_start = &file_thread_; +#if defined(OS_WIN) + // On Windows, the FILE thread needs to be have a UI message loop + // which pumps messages in such a way that Google Update can + // communicate back to us. + options = &ui_message_loop_options; +#else + options = &io_message_loop_options; +#endif + break; + case BrowserThread::PROCESS_LAUNCHER: + thread_to_start = &process_launcher_thread_; + break; + case BrowserThread::CACHE: + thread_to_start = &cache_thread_; + options = &io_message_loop_options; + break; + case BrowserThread::IO: + thread_to_start = &io_thread_; + options = &io_message_loop_options; + break; +#if defined(OS_CHROMEOS) + case BrowserThread::WEB_SOCKET_PROXY: + thread_to_start = &web_socket_proxy_thread_; + options = &io_message_loop_options; + break; +#endif + case BrowserThread::UI: + case BrowserThread::ID_COUNT: + default: + NOTREACHED(); + break; + } + + BrowserThread::ID id = static_cast<BrowserThread::ID>(thread_id); + + if (parts_.get()) + parts_->PreStartThread(id); + + if (thread_to_start) { + (*thread_to_start).reset(new BrowserProcessSubThread(id)); + (*thread_to_start)->StartWithOptions(*options); + } + + if (parts_.get()) + parts_->PostStartThread(id); + } + + if (parts_.get()) parts_->PreMainMessageLoopRun(); TRACE_EVENT_BEGIN_ETW("BrowserMain:MESSAGE_LOOP", 0, ""); @@ -281,8 +392,96 @@ void BrowserMainLoop::RunMainMessageLoopParts( if (completed_main_message_loop) *completed_main_message_loop = true; + ShutdownThreadsAndCleanUp(); +} + +void BrowserMainLoop::ShutdownThreadsAndCleanUp() { + // Teardown may start in PostMainMessageLoopRun, and during teardown we + // need to be able to perform IO. + base::ThreadRestrictions::SetIOAllowed(true); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + NewRunnableFunction(&base::ThreadRestrictions::SetIOAllowed, true)); + if (parts_.get()) parts_->PostMainMessageLoopRun(); + + // Must be size_t so we can subtract from it. + for (size_t thread_id = BrowserThread::ID_COUNT - 1; + thread_id >= (BrowserThread::UI + 1); + --thread_id) { + // Find the thread object we want to stop. Looping over all valid + // BrowserThread IDs and DCHECKing on a missing case in the switch + // statement helps avoid a mismatch between this code and the + // BrowserThread::ID enumeration. + // + // The destruction order is the reverse order of occurrence in the + // BrowserThread::ID list. The rationale for the order is as + // follows (need to be filled in a bit): + // + // - (Not sure why the WEB_SOCKET_PROXY thread is stopped first.) + // + // - The IO thread is the only user of the CACHE thread. + // + // - The PROCESS_LAUNCHER thread must be stopped after IO in case + // the IO thread posted a task to terminate a process on the + // process launcher thread. + // + // - (Not sure why FILE needs to stop before WEBKIT.) + // + // - The WEBKIT thread (which currently is the responsibility of + // the embedder to stop, by destroying ResourceDispatcherHost + // before the DB thread is stopped) + // + // - (Not sure why DB stops last.) + scoped_ptr<BrowserProcessSubThread>* thread_to_stop = NULL; + switch (thread_id) { + case BrowserThread::DB: + thread_to_stop = &db_thread_; + break; + case BrowserThread::WEBKIT: + // For now, the WebKit thread in the browser is owned by + // ResourceDispatcherHost, not by the content framework. Until + // this is fixed, we don't stop the thread but still call + // Pre/PostStopThread for the ID. + break; + case BrowserThread::FILE: + thread_to_stop = &file_thread_; + break; + case BrowserThread::PROCESS_LAUNCHER: + thread_to_stop = &process_launcher_thread_; + break; + case BrowserThread::CACHE: + thread_to_stop = &cache_thread_; + break; + case BrowserThread::IO: + thread_to_stop = &io_thread_; + break; +#if defined(OS_CHROMEOS) + case BrowserThread::WEB_SOCKET_PROXY: + thread_to_stop = &web_socket_proxy_thread_; + break; +#endif + case BrowserThread::UI: + case BrowserThread::ID_COUNT: + default: + NOTREACHED(); + break; + } + + BrowserThread::ID id = static_cast<BrowserThread::ID>(thread_id); + + if (parts_.get()) + parts_->PreStopThread(id); + if (thread_to_stop) + thread_to_stop->reset(); + if (parts_.get()) + parts_->PostStopThread(id); + } + + if (parts_.get()) + parts_->PostDestroyThreads(); } void BrowserMainLoop::InitializeMainThread() { diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h index d02d578..3038332 100644 --- a/content/browser/browser_main_loop.h +++ b/content/browser/browser_main_loop.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "content/browser/browser_process_sub_thread.h" class CommandLine; class HighResolutionTimerManager; @@ -25,6 +26,7 @@ class NetworkChangeNotifier; namespace content { class BrowserMainParts; +class BrowserShutdownImpl; class BrowserThreadImpl; struct MainFunctionParams; @@ -46,6 +48,13 @@ class BrowserMainLoop { int GetResultCode() const { return result_code_; } private: + // For ShutdownThreadsAndCleanUp. + friend class BrowserShutdownImpl; + + // Performs the shutdown sequence, starting with PostMainMessageLoopRun + // through stopping threads to PostDestroyThreads. + void ShutdownThreadsAndCleanUp(); + void InitializeMainThread(); // Members initialized on construction --------------------------------------- @@ -69,6 +78,14 @@ class BrowserMainLoop { // Members initialized in |InitializeMainThread()| --------------------------- // This must get destroyed before other threads that are created in parts_. scoped_ptr<BrowserThreadImpl> main_thread_; + scoped_ptr<BrowserProcessSubThread> io_thread_; + scoped_ptr<BrowserProcessSubThread> file_thread_; + scoped_ptr<BrowserProcessSubThread> db_thread_; + scoped_ptr<BrowserProcessSubThread> process_launcher_thread_; + scoped_ptr<BrowserProcessSubThread> cache_thread_; +#if defined(OS_CHROMEOS) + scoped_ptr<BrowserProcessSubThread> web_socket_proxy_thread_; +#endif DISALLOW_COPY_AND_ASSIGN(BrowserMainLoop); }; diff --git a/content/browser/browser_process_sub_thread.cc b/content/browser/browser_process_sub_thread.cc index 31c129e..7c77d7e 100644 --- a/content/browser/browser_process_sub_thread.cc +++ b/content/browser/browser_process_sub_thread.cc @@ -17,8 +17,6 @@ BrowserProcessSubThread::BrowserProcessSubThread(BrowserThread::ID identifier) : BrowserThreadImpl(identifier) {} BrowserProcessSubThread::~BrowserProcessSubThread() { - // We cannot rely on our base class to stop the thread since we want our - // CleanUp function to run. Stop(); } @@ -29,9 +27,13 @@ void BrowserProcessSubThread::Init() { #endif notification_service_ = new NotificationServiceImpl; + + BrowserThreadImpl::Init(); } void BrowserProcessSubThread::CleanUp() { + BrowserThreadImpl::CleanUp(); + delete notification_service_; notification_service_ = NULL; diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc index 023259b..0025f77 100644 --- a/content/browser/browser_thread_impl.cc +++ b/content/browser/browser_thread_impl.cc @@ -4,16 +4,19 @@ #include "content/browser/browser_thread_impl.h" +#include "base/atomicops.h" #include "base/bind.h" #include "base/lazy_instance.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" #include "base/threading/thread_restrictions.h" +namespace content { + namespace { // Friendly names for the well-known threads. -static const char* browser_thread_names[content::BrowserThread::ID_COUNT] = { +static const char* g_browser_thread_names[BrowserThread::ID_COUNT] = { "", // UI (name assembled in browser_main.cc). "Chrome_DBThread", // DB "Chrome_WebKitThread", // WEBKIT @@ -26,38 +29,86 @@ static const char* browser_thread_names[content::BrowserThread::ID_COUNT] = { #endif }; -} // namespace - -namespace content { - -namespace { - -// This lock protects |g_browser_threads|. Do not read or modify that array -// without holding this lock. Do not block while holding this lock. +// This lock protects |g_browser_threads|. Do not read or modify that +// array without holding this lock. Do not block while holding this +// lock. base::LazyInstance<base::Lock, base::LeakyLazyInstanceTraits<base::Lock> > g_lock = LAZY_INSTANCE_INITIALIZER; - -// An array of the BrowserThread objects. This array is protected by |g_lock|. -// The threads are not owned by this array. Typically, the threads are owned -// on the UI thread by the g_browser_process object. BrowserThreads remove +// This array is protected by |g_lock|. The threads are not owned by this +// array. Typically, the threads are owned on the UI thread by +// content::BrowserMainLoop. BrowserThreadImpl objects remove // themselves from this array upon destruction. -BrowserThread* g_browser_threads[BrowserThread::ID_COUNT]; +static BrowserThreadImpl* g_browser_threads[BrowserThread::ID_COUNT]; + +// Only atomic operations are used on this array. The delegates are +// not owned by this array, rather by whoever calls +// BrowserThread::SetDelegate. +static BrowserThreadDelegate* g_browser_thread_delegates[ + BrowserThread::ID_COUNT]; } // namespace -BrowserThreadImpl::BrowserThreadImpl(BrowserThread::ID identifier) - : BrowserThread(identifier) { +BrowserThreadImpl::BrowserThreadImpl(ID identifier) + : Thread(g_browser_thread_names[identifier]), + identifier_(identifier) { + Initialize(); } -BrowserThreadImpl::BrowserThreadImpl(BrowserThread::ID identifier, +BrowserThreadImpl::BrowserThreadImpl(ID identifier, MessageLoop* message_loop) - : BrowserThread(identifier, message_loop) { + : Thread(message_loop->thread_name().c_str()), + identifier_(identifier) { + set_message_loop(message_loop); + Initialize(); +} + +void BrowserThreadImpl::Init() { + using base::subtle::AtomicWord; + AtomicWord* storage = + reinterpret_cast<AtomicWord*>(&g_browser_thread_delegates[identifier_]); + AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); + BrowserThreadDelegate* delegate = + reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); + if (delegate) + delegate->Init(); +} + +void BrowserThreadImpl::CleanUp() { + using base::subtle::AtomicWord; + AtomicWord* storage = + reinterpret_cast<AtomicWord*>(&g_browser_thread_delegates[identifier_]); + AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); + BrowserThreadDelegate* delegate = + reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); + + if (delegate) + delegate->CleanUp(); +} + +void BrowserThreadImpl::Initialize() { + base::AutoLock lock(g_lock.Get()); + DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT); + DCHECK(g_browser_threads[identifier_] == NULL); + g_browser_threads[identifier_] = this; } BrowserThreadImpl::~BrowserThreadImpl() { + // All Thread subclasses must call Stop() in the destructor. This is + // doubly important here as various bits of code check they are on + // the right BrowserThread. Stop(); + + base::AutoLock lock(g_lock.Get()); + g_browser_threads[identifier_] = NULL; +#ifndef NDEBUG + // Double check that the threads are ordered correctly in the enumeration. + for (int i = identifier_ + 1; i < ID_COUNT; ++i) { + DCHECK(!g_browser_threads[i]) << + "Threads must be listed in the reverse order that they die"; + } +#endif } // static @@ -77,7 +128,7 @@ bool BrowserThreadImpl::PostTaskHelper( BrowserThread::ID current_thread; bool guaranteed_to_outlive_target_thread = GetCurrentThreadIdentifier(¤t_thread) && - current_thread >= identifier; + current_thread <= identifier; if (!guaranteed_to_outlive_target_thread) g_lock.Get().Acquire(); @@ -118,7 +169,7 @@ bool BrowserThreadImpl::PostTaskHelper( BrowserThread::ID current_thread; bool guaranteed_to_outlive_target_thread = GetCurrentThreadIdentifier(¤t_thread) && - current_thread >= identifier; + current_thread <= identifier; if (!guaranteed_to_outlive_target_thread) g_lock.Get().Acquire(); @@ -139,18 +190,6 @@ bool BrowserThreadImpl::PostTaskHelper( return !!message_loop; } -// TODO(joi): Remove -DeprecatedBrowserThread::DeprecatedBrowserThread(BrowserThread::ID identifier) - : BrowserThread(identifier) { -} -DeprecatedBrowserThread::DeprecatedBrowserThread(BrowserThread::ID identifier, - MessageLoop* message_loop) - : BrowserThread(identifier, message_loop) { -} -DeprecatedBrowserThread::~DeprecatedBrowserThread() { - Stop(); -} - // An implementation of MessageLoopProxy to be used in conjunction // with BrowserThread. class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy { @@ -215,44 +254,6 @@ class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy { DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy); }; -BrowserThread::BrowserThread(ID identifier) - : Thread(browser_thread_names[identifier]), - identifier_(identifier) { - Initialize(); -} - -BrowserThread::BrowserThread(ID identifier, - MessageLoop* message_loop) - : Thread(message_loop->thread_name().c_str()), - identifier_(identifier) { - set_message_loop(message_loop); - Initialize(); -} - -void BrowserThread::Initialize() { - base::AutoLock lock(g_lock.Get()); - DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT); - DCHECK(g_browser_threads[identifier_] == NULL); - g_browser_threads[identifier_] = this; -} - -BrowserThread::~BrowserThread() { - // Stop the thread here, instead of the parent's class destructor. This is so - // that if there are pending tasks that run, code that checks that it's on the - // correct BrowserThread succeeds. - Stop(); - - base::AutoLock lock(g_lock.Get()); - g_browser_threads[identifier_] = NULL; -#ifndef NDEBUG - // Double check that the threads are ordered correctly in the enumeration. - for (int i = identifier_ + 1; i < ID_COUNT; ++i) { - DCHECK(!g_browser_threads[i]) << - "Threads must be listed in the reverse order that they die"; - } -#endif -} - // static bool BrowserThread::IsWellKnownThread(ID identifier) { base::AutoLock lock(g_lock.Get()); @@ -393,4 +394,23 @@ BrowserThread::GetMessageLoopProxyForThread( return proxy; } +base::Thread* BrowserThread::UnsafeGetBrowserThread(ID identifier) { + base::AutoLock lock(g_lock.Get()); + base::Thread* thread = g_browser_threads[identifier]; + DCHECK(thread); + return thread; +} + +void BrowserThread::SetDelegate(ID identifier, + BrowserThreadDelegate* delegate) { + using base::subtle::AtomicWord; + AtomicWord* storage = reinterpret_cast<AtomicWord*>( + &g_browser_thread_delegates[identifier]); + AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange( + storage, reinterpret_cast<AtomicWord>(delegate)); + + // This catches registration when previously registered. + DCHECK(!delegate || !old_pointer); +} + } // namespace content diff --git a/content/browser/browser_thread_impl.h b/content/browser/browser_thread_impl.h index e24b385..d00ff5b 100644 --- a/content/browser/browser_thread_impl.h +++ b/content/browser/browser_thread_impl.h @@ -6,22 +6,33 @@ #define CONTENT_BROWSER_BROWSER_THREAD_IMPL_H_ #pragma once +#include "base/synchronization/lock.h" +#include "base/threading/thread.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h" namespace content { -class CONTENT_EXPORT BrowserThreadImpl : public BrowserThread { +class CONTENT_EXPORT BrowserThreadImpl + : public BrowserThread, public base::Thread { public: + // Construct a BrowserThreadImpl with the supplied identifier. It is an error + // to construct a BrowserThreadImpl that already exists. explicit BrowserThreadImpl(BrowserThread::ID identifier); + + // Special constructor for the main (UI) thread and unittests. We use a dummy + // thread here since the main thread already exists. BrowserThreadImpl(BrowserThread::ID identifier, MessageLoop* message_loop); virtual ~BrowserThreadImpl(); + protected: + virtual void Init() OVERRIDE; + virtual void CleanUp() OVERRIDE; + private: - // We implement most functionality on the public set of - // BrowserThread functions, but state is stored in the - // BrowserThreadImpl to keep the public API cleaner. Therefore make - // BrowserThread a friend class. + // We implement all the functionality of the public BrowserThread + // functions, but state is stored in the BrowserThreadImpl to keep + // the API cleaner. Therefore make BrowserThread a friend class. friend class BrowserThread; // TODO(brettw) remove this variant when Task->Closure migration is complete. @@ -37,6 +48,13 @@ class CONTENT_EXPORT BrowserThreadImpl : public BrowserThread { const base::Closure& task, int64 delay_ms, bool nestable); + + // Common initialization code for the constructors. + void Initialize(); + + // The identifier of this thread. Only one thread can exist with a given + // identifier at a given time. + ID identifier_; }; } // namespace content |