Index: content/browser/browser_main_loop.cc |
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc |
index dd93e586511e573cc9cf26bc7c154e2f66a8dc75..1a475130ae4cb2873e85c3a6d9a3267b907f9a73 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,37 @@ 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 |
+ } |
+}; |
+ |
+namespace browser_shutdown { |
+ |
+void ImmediateShutdownAndExitProcess() { |
+ BrowserShutdownImpl::ImmediateShutdownAndExitProcess(); |
+} |
+ |
+} // namespace browser_shutdown |
// BrowserMainLoop construction / destructione ============================= |
@@ -152,12 +184,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 |
@@ -264,6 +300,41 @@ void BrowserMainLoop::RunMainMessageLoopParts( |
if (parts_.get()) |
parts_->PreMainMessageLoopRun(); |
+ 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; |
+ |
+ db_thread_.reset(new BrowserProcessSubThread(BrowserThread::DB)); |
+ db_thread_->Start(); |
+ |
+ file_thread_.reset(new BrowserProcessSubThread(BrowserThread::FILE)); |
+#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. |
+ file_thread_->StartWithOptions(ui_message_loop_options); |
+#else |
+ file_thread_->StartWithOptions(io_message_loop_options); |
+#endif |
+ |
+ process_launcher_thread_.reset( |
+ new BrowserProcessSubThread(BrowserThread::PROCESS_LAUNCHER)); |
+ process_launcher_thread_->Start(); |
+ |
+ cache_thread_.reset(new BrowserProcessSubThread(BrowserThread::CACHE)); |
+ cache_thread_->StartWithOptions(io_message_loop_options); |
+ io_thread_.reset(new BrowserProcessSubThread(BrowserThread::IO)); |
+ io_thread_->StartWithOptions(io_message_loop_options); |
+#if defined(OS_CHROMEOS) |
+ web_socket_proxy_thread_.reset( |
+ new BrowserProcessSubThread(BrowserThread::WEB_SOCKET_PROXY)); |
+ web_socket_proxy_thread_->StartWithOptions(io_message_loop_options); |
+#endif |
+ |
+ if (parts_.get()) |
+ parts_->PreMainMessageLoopRunThreadsCreated(); |
+ |
TRACE_EVENT_BEGIN_ETW("BrowserMain:MESSAGE_LOOP", 0, ""); |
// If the UI thread blocks, the whole UI is unresponsive. |
// Do not allow disk IO from the UI thread. |
@@ -281,8 +352,95 @@ 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::DB; |
+ --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::UI: |
+ case BrowserThread::ID_COUNT: |
+ NOTREACHED(); |
+ break; |
+ 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 |
+ } |
+ |
+ 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_->FinalCleanup(); |
} |
void BrowserMainLoop::InitializeMainThread() { |