summaryrefslogtreecommitdiffstats
path: root/content/browser
diff options
context:
space:
mode:
authorjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-28 15:56:41 +0000
committerjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-28 15:56:41 +0000
commit2e5b60a280076017d700e56a5cb81ec62149a9bc (patch)
treee1f212d0f3cbdb7c04f4fd8fc67e1fcd26c350da /content/browser
parente04be707101e60bb5512c69a64ce5b64a1c4eeba (diff)
downloadchromium_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.cc199
-rw-r--r--content/browser/browser_main_loop.h17
-rw-r--r--content/browser/browser_process_sub_thread.cc6
-rw-r--r--content/browser/browser_thread_impl.cc160
-rw-r--r--content/browser/browser_thread_impl.h28
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(&current_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(&current_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