summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorglider <glider@chromium.org>2015-05-18 06:24:19 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-18 13:24:21 +0000
commit787e3347eb49a59db5e6fe0374f50464f36d48e3 (patch)
tree65004507e5713d6f9c8e275b8829b61c86715da0
parentcfdf46013b37da38c878632b87fa24f1b7997f3d (diff)
downloadchromium_src-787e3347eb49a59db5e6fe0374f50464f36d48e3.zip
chromium_src-787e3347eb49a59db5e6fe0374f50464f36d48e3.tar.gz
chromium_src-787e3347eb49a59db5e6fe0374f50464f36d48e3.tar.bz2
Revert of Reland: Lazily initialize MessageLoop for faster thread startup (patchset #5 id:160001 of https://codereview.chromium.org/1129953004/)
Reason for revert: Massive data race reports, see https://crbug.com/489263 Original issue's description: > Reland: Lazily initialize MessageLoop for faster thread startup > > Original review: https://codereview.chromium.org/1011683002/ > > Reverted because it's suspected for following flakiness issues: > http://crbug.com/485157 - Windows race > http://crbug.com/485091 - Android ThreadWatcher > http://crbug.com/485178 - interactive_ui_tests Menu* tests > > PS1 is the original patch set that gets reverted. > > BUG=465458, 485157, 485091, 485178 > TBR=jam > > Committed: https://crrev.com/8b6133a69f16702a32a3c3104630c4d9ac393b7a > Cr-Commit-Position: refs/heads/master@{#330329} TBR=thakis@chromium.org,toyoshim@chromium.org,jam@chromium.org,kinuko@chromium.org NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=465458, 485157, 485091, 485178 Review URL: https://codereview.chromium.org/1140363002 Cr-Commit-Position: refs/heads/master@{#330351}
-rw-r--r--base/message_loop/incoming_task_queue.cc36
-rw-r--r--base/message_loop/incoming_task_queue.h10
-rw-r--r--base/message_loop/message_loop.cc80
-rw-r--r--base/message_loop/message_loop.h43
-rw-r--r--base/message_loop/message_loop_proxy_impl.cc7
-rw-r--r--base/message_loop/message_loop_proxy_impl.h3
-rw-r--r--base/message_loop/message_pump_perftest.cc5
-rw-r--r--base/threading/platform_thread.h4
-rw-r--r--base/threading/platform_thread_win.cc5
-rw-r--r--base/threading/thread.cc174
-rw-r--r--base/threading/thread.h37
-rw-r--r--base/threading/thread_id_name_manager_unittest.cc12
-rw-r--r--base/threading/thread_unittest.cc5
-rw-r--r--chrome/browser/io_thread.cc5
-rw-r--r--chrome/browser/io_thread.h1
-rw-r--r--chrome/browser/metrics/thread_watcher_android_unittest.cc2
-rw-r--r--chrome/browser/metrics/thread_watcher_unittest.cc8
-rw-r--r--chrome/browser/sync/glue/sync_backend_registrar_unittest.cc3
-rw-r--r--content/browser/browser_thread_impl.cc8
-rw-r--r--content/public/browser/browser_thread_delegate.h3
-rw-r--r--content/public/test/test_browser_thread.cc4
-rw-r--r--content/public/test/test_browser_thread.h4
-rw-r--r--net/android/network_change_notifier_android.cc3
-rw-r--r--net/test/embedded_test_server/embedded_test_server.cc1
24 files changed, 180 insertions, 283 deletions
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc
index 5e9a461..c1ce939 100644
--- a/base/message_loop/incoming_task_queue.cc
+++ b/base/message_loop/incoming_task_queue.cc
@@ -44,8 +44,7 @@ IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
message_loop_(message_loop),
next_sequence_num_(0),
message_loop_scheduled_(false),
- always_schedule_work_(AlwaysNotifyPump(message_loop_->type())),
- is_ready_for_scheduling_(false) {
+ always_schedule_work_(AlwaysNotifyPump(message_loop_->type())) {
}
bool IncomingTaskQueue::AddToIncomingQueue(
@@ -110,15 +109,6 @@ void IncomingTaskQueue::WillDestroyCurrentMessageLoop() {
message_loop_ = NULL;
}
-void IncomingTaskQueue::StartScheduling() {
- AutoLock lock(incoming_queue_lock_);
- DCHECK(!is_ready_for_scheduling_);
- DCHECK(!message_loop_scheduled_);
- is_ready_for_scheduling_ = true;
- if (!incoming_queue_.empty())
- ScheduleWork();
-}
-
IncomingTaskQueue::~IncomingTaskQueue() {
// Verify that WillDestroyCurrentMessageLoop() has been called.
DCHECK(!message_loop_);
@@ -158,25 +148,19 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
incoming_queue_.push(*pending_task);
pending_task->task.Reset();
- if (is_ready_for_scheduling_ &&
- (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
- ScheduleWork();
+ if (always_schedule_work_ || (!message_loop_scheduled_ && was_empty)) {
+ // Wake up the message loop.
+ message_loop_->ScheduleWork();
+ // After we've scheduled the message loop, we do not need to do so again
+ // until we know it has processed all of the work in our queue and is
+ // waiting for more work again. The message loop will always attempt to
+ // reload from the incoming queue before waiting again so we clear this flag
+ // in ReloadWorkQueue().
+ message_loop_scheduled_ = true;
}
return true;
}
-void IncomingTaskQueue::ScheduleWork() {
- DCHECK(is_ready_for_scheduling_);
- // Wake up the message loop.
- message_loop_->ScheduleWork();
- // After we've scheduled the message loop, we do not need to do so again
- // until we know it has processed all of the work in our queue and is
- // waiting for more work again. The message loop will always attempt to
- // reload from the incoming queue before waiting again so we clear this flag
- // in ReloadWorkQueue().
- message_loop_scheduled_ = true;
-}
-
} // namespace internal
} // namespace base
diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h
index 7dd1e82..72e1f30 100644
--- a/base/message_loop/incoming_task_queue.h
+++ b/base/message_loop/incoming_task_queue.h
@@ -53,10 +53,6 @@ class BASE_EXPORT IncomingTaskQueue
// Disconnects |this| from the parent message loop.
void WillDestroyCurrentMessageLoop();
- // This should be called when the message loop becomes ready for
- // scheduling work.
- void StartScheduling();
-
private:
friend class RefCountedThreadSafe<IncomingTaskQueue>;
virtual ~IncomingTaskQueue();
@@ -70,9 +66,6 @@ class BASE_EXPORT IncomingTaskQueue
// does not retain |pending_task->task| beyond this function call.
bool PostPendingTask(PendingTask* pending_task);
- // Wakes up the message loop and schedules work.
- void ScheduleWork();
-
// Number of tasks that require high resolution timing. This value is kept
// so that ReloadWorkQueue() completes in constant time.
int high_res_task_count_;
@@ -99,9 +92,6 @@ class BASE_EXPORT IncomingTaskQueue
// if the incoming queue was not empty.
const bool always_schedule_work_;
- // False until StartScheduling() is called.
- bool is_ready_for_scheduling_;
-
DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue);
};
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 4222c77..eb0f968 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -100,10 +100,6 @@ MessagePumpForIO* ToPumpIO(MessagePump* pump) {
}
#endif // !defined(OS_NACL_SFI)
-scoped_ptr<MessagePump> ReturnPump(scoped_ptr<MessagePump> pump) {
- return pump;
-}
-
} // namespace
//------------------------------------------------------------------------------
@@ -120,19 +116,41 @@ MessageLoop::DestructionObserver::~DestructionObserver() {
//------------------------------------------------------------------------------
MessageLoop::MessageLoop(Type type)
- : MessageLoop(type, MessagePumpFactoryCallback()) {
- BindToCurrentThread();
+ : type_(type),
+#if defined(OS_WIN)
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
+#endif
+ nestable_tasks_allowed_(true),
+#if defined(OS_WIN)
+ os_modal_loop_(false),
+#endif // OS_WIN
+ message_histogram_(NULL),
+ run_loop_(NULL) {
+ Init();
+
+ pump_ = CreateMessagePumpForType(type).Pass();
}
MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
- : MessageLoop(TYPE_CUSTOM, Bind(&ReturnPump, Passed(&pump))) {
- BindToCurrentThread();
+ : pump_(pump.Pass()),
+ type_(TYPE_CUSTOM),
+#if defined(OS_WIN)
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
+#endif
+ nestable_tasks_allowed_(true),
+#if defined(OS_WIN)
+ os_modal_loop_(false),
+#endif // OS_WIN
+ message_histogram_(NULL),
+ run_loop_(NULL) {
+ DCHECK(pump_.get());
+ Init();
}
MessageLoop::~MessageLoop() {
- // current() could be NULL if this message loop is destructed before it is
- // bound to a thread.
- DCHECK(current() == this || !current());
+ DCHECK_EQ(this, current());
// iOS just attaches to the loop, it doesn't Run it.
// TODO(stuartmorgan): Consider wiring up a Detach().
@@ -281,13 +299,11 @@ void MessageLoop::PostNonNestableDelayedTask(
}
void MessageLoop::Run() {
- DCHECK(pump_);
RunLoop run_loop;
run_loop.Run();
}
void MessageLoop::RunUntilIdle() {
- DCHECK(pump_);
RunLoop run_loop;
run_loop.RunUntilIdle();
}
@@ -367,43 +383,13 @@ bool MessageLoop::IsIdleForTesting() {
//------------------------------------------------------------------------------
-scoped_ptr<MessageLoop> MessageLoop::CreateUnbound(
- Type type, MessagePumpFactoryCallback pump_factory) {
- return make_scoped_ptr(new MessageLoop(type, pump_factory));
-}
-
-MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory)
- : type_(type),
-#if defined(OS_WIN)
- pending_high_res_tasks_(0),
- in_high_res_mode_(false),
-#endif
- nestable_tasks_allowed_(true),
-#if defined(OS_WIN)
- os_modal_loop_(false),
-#endif // OS_WIN
- pump_factory_(pump_factory),
- message_histogram_(NULL),
- run_loop_(NULL),
- incoming_task_queue_(new internal::IncomingTaskQueue(this)),
- message_loop_proxy_(
- new internal::MessageLoopProxyImpl(incoming_task_queue_)) {
- // If type is TYPE_CUSTOM non-null pump_factory must be given.
- DCHECK_EQ(type_ == TYPE_CUSTOM, !pump_factory_.is_null());
-}
-
-void MessageLoop::BindToCurrentThread() {
- DCHECK(!pump_);
- if (!pump_factory_.is_null())
- pump_ = pump_factory_.Run();
- else
- pump_ = CreateMessagePumpForType(type_);
-
+void MessageLoop::Init() {
DCHECK(!current()) << "should only have one message loop per thread";
lazy_tls_ptr.Pointer()->Set(this);
- incoming_task_queue_->StartScheduling();
- message_loop_proxy_->BindToCurrentThread();
+ incoming_task_queue_ = new internal::IncomingTaskQueue(this);
+ message_loop_proxy_ =
+ new internal::MessageLoopProxyImpl(incoming_task_queue_);
thread_task_runner_handle_.reset(
new ThreadTaskRunnerHandle(message_loop_proxy_));
}
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index f2f89d0..fd7596a 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -114,8 +114,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
explicit MessageLoop(Type type = TYPE_DEFAULT);
// Creates a TYPE_CUSTOM MessageLoop with the supplied MessagePump, which must
// be non-NULL.
- explicit MessageLoop(scoped_ptr<MessagePump> pump);
-
+ explicit MessageLoop(scoped_ptr<base::MessagePump> pump);
~MessageLoop() override;
// Returns the MessageLoop object for the current thread, or null if none.
@@ -395,6 +394,10 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
+ // Wakes up the message pump. Can be called on any thread. The caller is
+ // responsible for synchronizing ScheduleWork() calls.
+ void ScheduleWork();
+
// Returns the TaskAnnotator which is used to add debug information to posted
// tasks.
debug::TaskAnnotator* task_annotator() { return &task_annotator_; }
@@ -408,33 +411,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
private:
friend class RunLoop;
- friend class internal::IncomingTaskQueue;
- friend class ScheduleWorkTest;
- friend class Thread;
-
- using MessagePumpFactoryCallback = Callback<scoped_ptr<MessagePump>()>;
- // Creates a MessageLoop without binding to a thread.
- // If |type| is TYPE_CUSTOM non-null |pump_factory| must be also given
- // to create a message pump for this message loop. Otherwise a default
- // message pump for the |type| is created.
- //
- // It is valid to call this to create a new message loop on one thread,
- // and then pass it to the thread where the message loop actually runs.
- // The message loop's BindToCurrentThread() method must be called on the
- // thread the message loop runs on, before calling Run().
- // Before BindToCurrentThread() is called only Post*Task() functions can
- // be called on the message loop.
- scoped_ptr<MessageLoop> CreateUnbound(
- Type type,
- MessagePumpFactoryCallback pump_factory);
-
- // Common private constructor. Other constructors delegate the initialization
- // to this constructor.
- MessageLoop(Type type, MessagePumpFactoryCallback pump_factory);
-
- // Configure various members and bind this message loop to the current thread.
- void BindToCurrentThread();
+ // Configures various members for the two constructors.
+ void Init();
// Invokes the actual run loop using the message pump.
void RunHandler();
@@ -458,10 +437,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// empty.
void ReloadWorkQueue();
- // Wakes up the message pump. Can be called on any thread. The caller is
- // responsible for synchronizing ScheduleWork() calls.
- void ScheduleWork();
-
// Start recording histogram info about events and action IF it was enabled
// and IF the statistics recorder can accept a registration of our histogram.
void StartHistogrammer();
@@ -515,10 +490,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
bool os_modal_loop_;
#endif
- // pump_factory_.Run() is called to create a message pump for this loop
- // if type_ is TYPE_CUSTOM and pump_ is null.
- MessagePumpFactoryCallback pump_factory_;
-
std::string thread_name_;
// A profiling histogram showing the counts of various messages and events.
HistogramBase* message_histogram_;
diff --git a/base/message_loop/message_loop_proxy_impl.cc b/base/message_loop/message_loop_proxy_impl.cc
index 0da21a0..b7abca3 100644
--- a/base/message_loop/message_loop_proxy_impl.cc
+++ b/base/message_loop/message_loop_proxy_impl.cc
@@ -15,12 +15,7 @@ namespace internal {
MessageLoopProxyImpl::MessageLoopProxyImpl(
scoped_refptr<IncomingTaskQueue> incoming_queue)
: incoming_queue_(incoming_queue),
- valid_thread_id_(kInvalidThreadId) {
-}
-
-void MessageLoopProxyImpl::BindToCurrentThread() {
- DCHECK_EQ(kInvalidThreadId, valid_thread_id_);
- valid_thread_id_ = PlatformThread::CurrentId();
+ valid_thread_id_(PlatformThread::CurrentId()) {
}
bool MessageLoopProxyImpl::PostDelayedTask(
diff --git a/base/message_loop/message_loop_proxy_impl.h b/base/message_loop/message_loop_proxy_impl.h
index c477320..0fe629f 100644
--- a/base/message_loop/message_loop_proxy_impl.h
+++ b/base/message_loop/message_loop_proxy_impl.h
@@ -24,9 +24,6 @@ class BASE_EXPORT MessageLoopProxyImpl : public MessageLoopProxy {
explicit MessageLoopProxyImpl(
scoped_refptr<IncomingTaskQueue> incoming_queue);
- // Initialize this message loop proxy on the current thread.
- void BindToCurrentThread();
-
// MessageLoopProxy implementation
bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
index 0bf3d0c..b3e5604 100644
--- a/base/message_loop/message_pump_perftest.cc
+++ b/base/message_loop/message_pump_perftest.cc
@@ -20,6 +20,7 @@
#endif
namespace base {
+namespace {
class ScheduleWorkTest : public testing::Test {
public:
@@ -223,6 +224,9 @@ TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
}
#endif
+static void DoNothing() {
+}
+
class FakeMessagePump : public MessagePump {
public:
FakeMessagePump() {}
@@ -285,4 +289,5 @@ TEST_F(PostTaskTest, OneHundredTasksPerReload) {
Run(1000, 100);
}
+} // namespace
} // namespace base
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h
index 69a2b0d..d8f06e5 100644
--- a/base/threading/platform_thread.h
+++ b/base/threading/platform_thread.h
@@ -89,10 +89,6 @@ class PlatformThreadHandle {
id_(id) {
}
- PlatformThreadId id() const {
- return id_;
- }
-
bool is_equal(const PlatformThreadHandle& other) const {
return handle_ == other.handle_;
}
diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc
index aeaa7c7..4eb2cb2 100644
--- a/base/threading/platform_thread_win.cc
+++ b/base/threading/platform_thread_win.cc
@@ -108,16 +108,15 @@ bool CreateThreadInternal(size_t stack_size,
// have to work running on CreateThread() threads anyway, since we run code
// on the Windows thread pool, etc. For some background on the difference:
// http://www.microsoft.com/msj/1099/win32/win321099.aspx
- PlatformThreadId thread_id;
void* thread_handle = CreateThread(
- NULL, stack_size, ThreadFunc, params, flags, &thread_id);
+ NULL, stack_size, ThreadFunc, params, flags, NULL);
if (!thread_handle) {
delete params;
return false;
}
if (out_thread_handle)
- *out_thread_handle = PlatformThreadHandle(thread_handle, thread_id);
+ *out_thread_handle = PlatformThreadHandle(thread_handle);
else
CloseHandle(thread_handle);
return true;
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index b753a6b..0e4aab2 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -37,6 +37,20 @@ void ThreadQuitHelper() {
Thread::SetThreadWasQuitProperly(true);
}
+// Used to pass data to ThreadMain. This structure is allocated on the stack
+// from within StartWithOptions.
+struct Thread::StartupData {
+ // We get away with a const reference here because of how we are allocated.
+ const Thread::Options& options;
+
+ // Used to synchronize thread startup.
+ WaitableEvent event;
+
+ explicit StartupData(const Options& opt)
+ : options(opt),
+ event(false, false) {}
+};
+
Thread::Options::Options()
: message_loop_type(MessageLoop::TYPE_DEFAULT),
timer_slack(TIMER_SLACK_NONE),
@@ -58,11 +72,13 @@ Thread::Thread(const std::string& name)
#if defined(OS_WIN)
com_status_(NONE),
#endif
+ started_(false),
stopping_(false),
running_(false),
+ startup_data_(NULL),
thread_(0),
- message_loop_(nullptr),
- message_loop_timer_slack_(TIMER_SLACK_NONE),
+ message_loop_(NULL),
+ thread_id_(kInvalidThreadId),
name_(name) {
}
@@ -88,50 +104,34 @@ bool Thread::StartWithOptions(const Options& options) {
SetThreadWasQuitProperly(false);
- MessageLoop::Type type = options.message_loop_type;
- if (!options.message_pump_factory.is_null())
- type = MessageLoop::TYPE_CUSTOM;
-
- message_loop_timer_slack_ = options.timer_slack;
- message_loop_ = new MessageLoop(type, options.message_pump_factory);
-
- start_event_.reset(new WaitableEvent(false, false));
+ StartupData startup_data(options);
+ startup_data_ = &startup_data;
- // Hold the thread_lock_ while starting a new thread, so that we can make sure
- // that thread_ is populated before the newly created thread accesses it.
- {
- AutoLock lock(thread_lock_);
- if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
- DLOG(ERROR) << "failed to create thread";
- delete message_loop_;
- message_loop_ = nullptr;
- start_event_.reset();
- return false;
- }
+ if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
+ DLOG(ERROR) << "failed to create thread";
+ startup_data_ = NULL;
+ return false;
}
- DCHECK(message_loop_);
- return true;
-}
-
-bool Thread::StartAndWaitForTesting() {
- bool result = Start();
- if (!result)
- return false;
- WaitUntilThreadStarted();
- return true;
-}
+ // TODO(kinuko): Remove once crbug.com/465458 is solved.
+ tracked_objects::ScopedTracker tracking_profile_wait(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "465458 base::Thread::StartWithOptions (Wait)"));
-bool Thread::WaitUntilThreadStarted() {
- if (!start_event_)
- return false;
+ // Wait for the thread to start and initialize message_loop_
base::ThreadRestrictions::ScopedAllowWait allow_wait;
- start_event_->Wait();
+ startup_data.event.Wait();
+
+ // set it to NULL so we don't keep a pointer to some object on the stack.
+ startup_data_ = NULL;
+ started_ = true;
+
+ DCHECK(message_loop_);
return true;
}
void Thread::Stop() {
- if (!start_event_)
+ if (!started_)
return;
StopSoon();
@@ -147,7 +147,7 @@ void Thread::Stop() {
DCHECK(!message_loop_);
// The thread no longer needs to be joined.
- start_event_.reset();
+ started_ = false;
stopping_ = false;
}
@@ -155,7 +155,9 @@ void Thread::Stop() {
void Thread::StopSoon() {
// We should only be called on the same thread that started us.
- DCHECK_NE(thread_id(), PlatformThread::CurrentId());
+ // Reading thread_id_ without a lock can lead to a benign data race
+ // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
+ DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());
if (stopping_ || !message_loop_)
return;
@@ -164,28 +166,14 @@ void Thread::StopSoon() {
task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
}
-PlatformThreadId Thread::thread_id() const {
- AutoLock lock(thread_lock_);
- return thread_.id();
-}
-
bool Thread::IsRunning() const {
- // If the thread's already started (i.e. message_loop_ is non-null) and
- // not yet requested to stop (i.e. stopping_ is false) we can just return
- // true. (Note that stopping_ is touched only on the same thread that
- // starts / started the new thread so we need no locking here.)
- if (message_loop_ && !stopping_)
- return true;
- // Otherwise check the running_ flag, which is set to true by the new thread
- // only while it is inside Run().
- AutoLock lock(running_lock_);
return running_;
}
void Thread::SetPriority(ThreadPriority priority) {
// The thread must be started (and id known) for this to be
// compatible with all platforms.
- DCHECK(message_loop_ != nullptr);
+ DCHECK_NE(thread_id_, kInvalidThreadId);
PlatformThread::SetThreadPriority(thread_, priority);
}
@@ -206,60 +194,60 @@ bool Thread::GetThreadWasQuitProperly() {
}
void Thread::ThreadMain() {
- // Complete the initialization of our Thread object.
- PlatformThread::SetName(name_.c_str());
- ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
+ {
+ // The message loop for this thread.
+ // Allocated on the heap to centralize any leak reports at this line.
+ scoped_ptr<MessageLoop> message_loop;
+ if (!startup_data_->options.message_pump_factory.is_null()) {
+ message_loop.reset(
+ new MessageLoop(startup_data_->options.message_pump_factory.Run()));
+ } else {
+ message_loop.reset(
+ new MessageLoop(startup_data_->options.message_loop_type));
+ }
- // Lazily initialize the message_loop so that it can run on this thread.
- DCHECK(message_loop_);
- scoped_ptr<MessageLoop> message_loop(message_loop_);
- message_loop_->BindToCurrentThread();
- message_loop_->set_thread_name(name_);
- message_loop_->SetTimerSlack(message_loop_timer_slack_);
+ // Complete the initialization of our Thread object.
+ thread_id_ = PlatformThread::CurrentId();
+ PlatformThread::SetName(name_);
+ ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
+ message_loop->set_thread_name(name_);
+ message_loop->SetTimerSlack(startup_data_->options.timer_slack);
+ message_loop_ = message_loop.get();
#if defined(OS_WIN)
- scoped_ptr<win::ScopedCOMInitializer> com_initializer;
- if (com_status_ != NONE) {
- com_initializer.reset((com_status_ == STA) ?
- new win::ScopedCOMInitializer() :
- new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
- }
+ scoped_ptr<win::ScopedCOMInitializer> com_initializer;
+ if (com_status_ != NONE) {
+ com_initializer.reset((com_status_ == STA) ?
+ new win::ScopedCOMInitializer() :
+ new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
+ }
#endif
- // Make sure the thread_id() returns current thread.
- // (This internally acquires lock against PlatformThread::Create)
- DCHECK_EQ(thread_id(), PlatformThread::CurrentId());
+ // Let the thread do extra initialization.
+ // Let's do this before signaling we are started.
+ Init();
- // Let the thread do extra initialization.
- Init();
-
- {
- AutoLock lock(running_lock_);
running_ = true;
- }
-
- start_event_->Signal();
+ startup_data_->event.Signal();
+ // startup_data_ can't be touched anymore since the starting thread is now
+ // unlocked.
- Run(message_loop_);
-
- {
- AutoLock lock(running_lock_);
+ Run(message_loop_);
running_ = false;
- }
- // Let the thread do extra cleanup.
- CleanUp();
+ // Let the thread do extra cleanup.
+ CleanUp();
#if defined(OS_WIN)
- com_initializer.reset();
+ com_initializer.reset();
#endif
- // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
- DCHECK(GetThreadWasQuitProperly());
+ // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
+ DCHECK(GetThreadWasQuitProperly());
- // We can't receive messages anymore.
- // (The message loop is destructed at the end of this block)
- message_loop_ = NULL;
+ // We can't receive messages anymore.
+ message_loop_ = NULL;
+ }
}
} // namespace base
diff --git a/base/threading/thread.h b/base/threading/thread.h
index 6b2f1c5..4915606 100644
--- a/base/threading/thread.h
+++ b/base/threading/thread.h
@@ -13,13 +13,11 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/timer_slack.h"
#include "base/single_thread_task_runner.h"
-#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
namespace base {
class MessagePump;
-class WaitableEvent;
// A simple thread abstraction that establishes a MessageLoop on a new thread.
// The consumer uses the MessageLoop of the thread to cause code to execute on
@@ -47,7 +45,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// This is ignored if message_pump_factory.is_null() is false.
MessageLoop::Type message_loop_type;
- // Specifies timer slack for thread message loop.
+ // Specify timer slack for thread message loop.
TimerSlack timer_slack;
// Used to create the MessagePump for the MessageLoop. The callback is Run()
@@ -83,7 +81,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// init_com_with_mta(false) and then StartWithOptions() with any message loop
// type other than TYPE_UI.
void init_com_with_mta(bool use_mta) {
- DCHECK(!start_event_);
+ DCHECK(!started_);
com_status_ = use_mta ? MTA : STA;
}
#endif
@@ -105,18 +103,6 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// callback.
bool StartWithOptions(const Options& options);
- // Starts the thread and wait for the thread to start and run initialization
- // before returning. It's same as calling Start() and then
- // WaitUntilThreadStarted().
- // Note that using this (instead of Start() or StartWithOptions() causes
- // jank on the calling thread, should be used only in testing code.
- bool StartAndWaitForTesting();
-
- // Blocks until the thread starts running. Called within StartAndWait().
- // Note that calling this causes jank on the calling thread, must be used
- // carefully for production code.
- bool WaitUntilThreadStarted();
-
// Signals the thread to exit and returns once the thread has exited. After
// this method returns, the Thread object is completely reset and may be used
// as if it were newly constructed (i.e., Start may be called again).
@@ -180,7 +166,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
PlatformThreadHandle thread_handle() { return thread_; }
// The thread ID.
- PlatformThreadId thread_id() const;
+ PlatformThreadId thread_id() const { return thread_id_; }
// Returns true if the thread has been started, and not yet stopped.
bool IsRunning() const;
@@ -222,32 +208,33 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
ComStatus com_status_;
#endif
+ // Whether we successfully started the thread.
+ bool started_;
+
// If true, we're in the middle of stopping, and shouldn't access
// |message_loop_|. It may non-NULL and invalid.
bool stopping_;
// True while inside of Run().
bool running_;
- mutable base::Lock running_lock_; // Protects running_.
+
+ // Used to pass data to ThreadMain.
+ struct StartupData;
+ StartupData* startup_data_;
// The thread's handle.
PlatformThreadHandle thread_;
- mutable base::Lock thread_lock_; // Protects thread_.
// The thread's message loop. Valid only while the thread is alive. Set
// by the created thread.
MessageLoop* message_loop_;
- // Stores Options::timer_slack_ until the message loop has been bound to
- // a thread.
- TimerSlack message_loop_timer_slack_;
+ // Our thread's ID.
+ PlatformThreadId thread_id_;
// The name of the thread. Used for debugging purposes.
std::string name_;
- // Non-null if the thread has successfully started.
- scoped_ptr<WaitableEvent> start_event_;
-
friend void ThreadQuitHelper();
DISALLOW_COPY_AND_ASSIGN(Thread);
diff --git a/base/threading/thread_id_name_manager_unittest.cc b/base/threading/thread_id_name_manager_unittest.cc
index b17c681..b5953d5 100644
--- a/base/threading/thread_id_name_manager_unittest.cc
+++ b/base/threading/thread_id_name_manager_unittest.cc
@@ -21,8 +21,8 @@ TEST_F(ThreadIdNameManagerTest, AddThreads) {
base::Thread thread_a(kAThread);
base::Thread thread_b(kBThread);
- thread_a.StartAndWaitForTesting();
- thread_b.StartAndWaitForTesting();
+ thread_a.Start();
+ thread_b.Start();
EXPECT_STREQ(kAThread, manager->GetName(thread_a.thread_id()));
EXPECT_STREQ(kBThread, manager->GetName(thread_b.thread_id()));
@@ -35,10 +35,10 @@ TEST_F(ThreadIdNameManagerTest, RemoveThreads) {
base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
base::Thread thread_a(kAThread);
- thread_a.StartAndWaitForTesting();
+ thread_a.Start();
{
base::Thread thread_b(kBThread);
- thread_b.StartAndWaitForTesting();
+ thread_b.Start();
thread_b.Stop();
}
EXPECT_STREQ(kAThread, manager->GetName(thread_a.thread_id()));
@@ -51,12 +51,12 @@ TEST_F(ThreadIdNameManagerTest, RestartThread) {
base::ThreadIdNameManager* manager = base::ThreadIdNameManager::GetInstance();
base::Thread thread_a(kAThread);
- thread_a.StartAndWaitForTesting();
+ thread_a.Start();
base::PlatformThreadId a_id = thread_a.thread_id();
EXPECT_STREQ(kAThread, manager->GetName(a_id));
thread_a.Stop();
- thread_a.StartAndWaitForTesting();
+ thread_a.Start();
EXPECT_STREQ("", manager->GetName(a_id));
EXPECT_STREQ(kAThread, manager->GetName(thread_a.thread_id()));
thread_a.Stop();
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index e86c758..a89768e 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -194,12 +194,11 @@ TEST_F(ThreadTest, ThreadName) {
EXPECT_EQ("ThreadName", a.thread_name());
}
-// Make sure Init() is called after Start() and before
-// WaitUntilThreadInitialized() returns.
+// Make sure we can't use a thread between Start() and Init().
TEST_F(ThreadTest, SleepInsideInit) {
SleepInsideInitThread t;
EXPECT_FALSE(t.InitCalled());
- t.StartAndWaitForTesting();
+ t.Start();
EXPECT_TRUE(t.InitCalled());
}
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 907ffbd..46d0e3e 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -608,6 +608,11 @@ net::URLRequestContextGetter* IOThread::system_url_request_context_getter() {
}
void IOThread::Init() {
+ // Prefer to use InitAsync unless you need initialization to block
+ // the UI thread
+}
+
+void IOThread::InitAsync() {
// TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466432
// is fixed.
tracked_objects::ScopedTracker tracking_profile1(
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index d31cb64..0b94438 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -255,6 +255,7 @@ class IOThread : public content::BrowserThreadDelegate {
// This handles initialization and destruction of state that must
// live on the IO thread.
void Init() override;
+ void InitAsync() override;
void CleanUp() override;
// Initializes |params| based on the settings in |globals|.
diff --git a/chrome/browser/metrics/thread_watcher_android_unittest.cc b/chrome/browser/metrics/thread_watcher_android_unittest.cc
index 2b5c04f..62489b6 100644
--- a/chrome/browser/metrics/thread_watcher_android_unittest.cc
+++ b/chrome/browser/metrics/thread_watcher_android_unittest.cc
@@ -48,7 +48,7 @@ TEST(ThreadWatcherAndroidTest, DISABLED_ApplicationStatusNotification) {
scoped_ptr<WatchDogThread> watchdog_thread_(new WatchDogThread());
- watchdog_thread_->StartAndWaitForTesting();
+ watchdog_thread_->Start();
EXPECT_FALSE(ThreadWatcherList::g_thread_watcher_list_);
diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc
index fcd2746..2884698 100644
--- a/chrome/browser/metrics/thread_watcher_unittest.cc
+++ b/chrome/browser/metrics/thread_watcher_unittest.cc
@@ -257,9 +257,9 @@ class ThreadWatcherTest : public ::testing::Test {
db_thread_.reset(new content::TestBrowserThread(BrowserThread::DB));
io_thread_.reset(new content::TestBrowserThread(BrowserThread::IO));
watchdog_thread_.reset(new WatchDogThread());
- db_thread_->StartAndWaitForTesting();
- io_thread_->StartAndWaitForTesting();
- watchdog_thread_->StartAndWaitForTesting();
+ db_thread_->Start();
+ io_thread_->Start();
+ watchdog_thread_->Start();
WatchDogThread::PostTask(
FROM_HERE,
@@ -697,7 +697,7 @@ TEST_F(ThreadWatcherListTest, Restart) {
content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop_for_ui);
scoped_ptr<WatchDogThread> watchdog_thread_(new WatchDogThread());
- watchdog_thread_->StartAndWaitForTesting();
+ watchdog_thread_->Start();
// See http://crbug.com/347887.
// StartWatchingAll() will PostDelayedTask to create g_thread_watcher_list_,
diff --git a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
index 34adb95..3db65e9 100644
--- a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc
@@ -336,9 +336,6 @@ TEST_F(SyncBackendRegistrarShutdownTest, BlockingShutdown) {
base::Bind(&SyncBackendRegistrar::Shutdown,
base::Unretained(registrar.release())));
- // Make sure the thread starts running.
- sync_thread->WaitUntilThreadStarted();
-
// The test verifies that the sync thread doesn't block because
// of the blocked DB thread and can finish the shutdown.
sync_thread->message_loop()->RunUntilIdle();
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index 4ae6607..78e3836 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -159,8 +159,14 @@ void BrowserThreadImpl::Init() {
AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
BrowserThreadDelegate* delegate =
reinterpret_cast<BrowserThreadDelegate*>(stored_pointer);
- if (delegate)
+ if (delegate) {
delegate->Init();
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&BrowserThreadDelegate::InitAsync,
+ // Delegate is expected to exist for the
+ // duration of the thread's lifetime
+ base::Unretained(delegate)));
+ }
}
// We disable optimizations for this block of functions so the compiler doesn't
diff --git a/content/public/browser/browser_thread_delegate.h b/content/public/browser/browser_thread_delegate.h
index 3688e65..ad5fd7e 100644
--- a/content/public/browser/browser_thread_delegate.h
+++ b/content/public/browser/browser_thread_delegate.h
@@ -22,6 +22,9 @@ class BrowserThreadDelegate {
// Called prior to starting the message loop
virtual void Init() = 0;
+ // Called as the first task on the thread's message loop.
+ virtual void InitAsync() = 0;
+
// Called just after the message loop ends.
virtual void CleanUp() = 0;
};
diff --git a/content/public/test/test_browser_thread.cc b/content/public/test/test_browser_thread.cc
index 62d9eb8..ac67480 100644
--- a/content/public/test/test_browser_thread.cc
+++ b/content/public/test/test_browser_thread.cc
@@ -58,10 +58,6 @@ bool TestBrowserThread::Start() {
return impl_->Start();
}
-bool TestBrowserThread::StartAndWaitForTesting() {
- return impl_->StartAndWaitForTesting();
-}
-
bool TestBrowserThread::StartIOThread() {
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
diff --git a/content/public/test/test_browser_thread.h b/content/public/test/test_browser_thread.h
index d3a2564..2428bbb 100644
--- a/content/public/test/test_browser_thread.h
+++ b/content/public/test/test_browser_thread.h
@@ -36,10 +36,6 @@ class TestBrowserThread {
// Starts the thread with a generic message loop.
bool Start();
- // Starts the thread with a generic message loop and waits for the
- // thread to run.
- bool StartAndWaitForTesting();
-
// Starts the thread with an IOThread message loop.
bool StartIOThread();
diff --git a/net/android/network_change_notifier_android.cc b/net/android/network_change_notifier_android.cc
index 9829235..a6ea628 100644
--- a/net/android/network_change_notifier_android.cc
+++ b/net/android/network_change_notifier_android.cc
@@ -173,9 +173,6 @@ NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
delegate_->AddObserver(this);
dns_config_service_thread_->StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
- // Wait until Init is called on the DNS config thread before
- // calling InitAfterStart.
- dns_config_service_thread_->WaitUntilThreadStarted();
dns_config_service_thread_->InitAfterStart();
}
diff --git a/net/test/embedded_test_server/embedded_test_server.cc b/net/test/embedded_test_server/embedded_test_server.cc
index e747c94..ac7d3bc 100644
--- a/net/test/embedded_test_server/embedded_test_server.cc
+++ b/net/test/embedded_test_server/embedded_test_server.cc
@@ -190,7 +190,6 @@ void EmbeddedTestServer::StartThread() {
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
io_thread_.reset(new base::Thread("EmbeddedTestServer io thread"));
CHECK(io_thread_->StartWithOptions(thread_options));
- CHECK(io_thread_->WaitUntilThreadStarted());
}
void EmbeddedTestServer::InitializeOnIOThread() {