diff options
author | kinuko <kinuko@chromium.org> | 2015-05-05 10:13:51 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-05 17:15:18 +0000 |
commit | f1f70cb5ebe24d85c575396f026a415ed0fe9afc (patch) | |
tree | 7707d3dbe7c3404c5391318bb015f990e4692d1e | |
parent | 8d20ad724b073dbb1e2b7e55f3174bdfc9ca2385 (diff) | |
download | chromium_src-f1f70cb5ebe24d85c575396f026a415ed0fe9afc.zip chromium_src-f1f70cb5ebe24d85c575396f026a415ed0fe9afc.tar.gz chromium_src-f1f70cb5ebe24d85c575396f026a415ed0fe9afc.tar.bz2 |
Lazily initialize MessageLoop for faster thread startup
Summary of the change and background discussion:
https://docs.google.com/a/chromium.org/document/d/1o1vUUOjX3tC7pV5-nxchaGtElo4NwtzKOAb4Zm09ezw/edit#
This implements approach 1 in the doc.
Approach 2: https://codereview.chromium.org/1086663002/
Approach 3: https://codereview.chromium.org/1058603004/
Discussion thread:
https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/2t6lB8hUgYw
BUG=465458
Review URL: https://codereview.chromium.org/1011683002
Cr-Commit-Position: refs/heads/master@{#328347}
22 files changed, 265 insertions, 177 deletions
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc index c1ce939..5e9a461 100644 --- a/base/message_loop/incoming_task_queue.cc +++ b/base/message_loop/incoming_task_queue.cc @@ -44,7 +44,8 @@ IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) message_loop_(message_loop), next_sequence_num_(0), message_loop_scheduled_(false), - always_schedule_work_(AlwaysNotifyPump(message_loop_->type())) { + always_schedule_work_(AlwaysNotifyPump(message_loop_->type())), + is_ready_for_scheduling_(false) { } bool IncomingTaskQueue::AddToIncomingQueue( @@ -109,6 +110,15 @@ 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_); @@ -148,19 +158,25 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { incoming_queue_.push(*pending_task); pending_task->task.Reset(); - 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; + if (is_ready_for_scheduling_ && + (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) { + ScheduleWork(); } 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 72e1f30..7dd1e82 100644 --- a/base/message_loop/incoming_task_queue.h +++ b/base/message_loop/incoming_task_queue.h @@ -53,6 +53,10 @@ 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(); @@ -66,6 +70,9 @@ 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_; @@ -92,6 +99,9 @@ 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 eb0f968..4222c77 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -100,6 +100,10 @@ MessagePumpForIO* ToPumpIO(MessagePump* pump) { } #endif // !defined(OS_NACL_SFI) +scoped_ptr<MessagePump> ReturnPump(scoped_ptr<MessagePump> pump) { + return pump; +} + } // namespace //------------------------------------------------------------------------------ @@ -116,41 +120,19 @@ MessageLoop::DestructionObserver::~DestructionObserver() { //------------------------------------------------------------------------------ MessageLoop::MessageLoop(Type type) - : 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(type, MessagePumpFactoryCallback()) { + BindToCurrentThread(); } MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump) - : 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(TYPE_CUSTOM, Bind(&ReturnPump, Passed(&pump))) { + BindToCurrentThread(); } MessageLoop::~MessageLoop() { - DCHECK_EQ(this, current()); + // current() could be NULL if this message loop is destructed before it is + // bound to a thread. + DCHECK(current() == this || !current()); // iOS just attaches to the loop, it doesn't Run it. // TODO(stuartmorgan): Consider wiring up a Detach(). @@ -299,11 +281,13 @@ 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(); } @@ -383,13 +367,43 @@ bool MessageLoop::IsIdleForTesting() { //------------------------------------------------------------------------------ -void MessageLoop::Init() { +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_); + DCHECK(!current()) << "should only have one message loop per thread"; lazy_tls_ptr.Pointer()->Set(this); - incoming_task_queue_ = new internal::IncomingTaskQueue(this); - message_loop_proxy_ = - new internal::MessageLoopProxyImpl(incoming_task_queue_); + incoming_task_queue_->StartScheduling(); + message_loop_proxy_->BindToCurrentThread(); 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 fd7596a..f2f89d0 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -114,7 +114,8 @@ 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<base::MessagePump> pump); + explicit MessageLoop(scoped_ptr<MessagePump> pump); + ~MessageLoop() override; // Returns the MessageLoop object for the current thread, or null if none. @@ -394,10 +395,6 @@ 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_; } @@ -411,9 +408,33 @@ 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>()>; - // Configures various members for the two constructors. - void Init(); + // 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(); // Invokes the actual run loop using the message pump. void RunHandler(); @@ -437,6 +458,10 @@ 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(); @@ -490,6 +515,10 @@ 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 b7abca3..0da21a0 100644 --- a/base/message_loop/message_loop_proxy_impl.cc +++ b/base/message_loop/message_loop_proxy_impl.cc @@ -15,7 +15,12 @@ namespace internal { MessageLoopProxyImpl::MessageLoopProxyImpl( scoped_refptr<IncomingTaskQueue> incoming_queue) : incoming_queue_(incoming_queue), - valid_thread_id_(PlatformThread::CurrentId()) { + valid_thread_id_(kInvalidThreadId) { +} + +void MessageLoopProxyImpl::BindToCurrentThread() { + DCHECK_EQ(kInvalidThreadId, valid_thread_id_); + 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 0fe629f..c477320 100644 --- a/base/message_loop/message_loop_proxy_impl.h +++ b/base/message_loop/message_loop_proxy_impl.h @@ -24,6 +24,9 @@ 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 b3e5604..0bf3d0c 100644 --- a/base/message_loop/message_pump_perftest.cc +++ b/base/message_loop/message_pump_perftest.cc @@ -20,7 +20,6 @@ #endif namespace base { -namespace { class ScheduleWorkTest : public testing::Test { public: @@ -224,9 +223,6 @@ TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) { } #endif -static void DoNothing() { -} - class FakeMessagePump : public MessagePump { public: FakeMessagePump() {} @@ -289,5 +285,4 @@ 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 d8f06e5..69a2b0d 100644 --- a/base/threading/platform_thread.h +++ b/base/threading/platform_thread.h @@ -89,6 +89,10 @@ 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 4eb2cb2..aeaa7c7 100644 --- a/base/threading/platform_thread_win.cc +++ b/base/threading/platform_thread_win.cc @@ -108,15 +108,16 @@ 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, NULL); + NULL, stack_size, ThreadFunc, params, flags, &thread_id); if (!thread_handle) { delete params; return false; } if (out_thread_handle) - *out_thread_handle = PlatformThreadHandle(thread_handle); + *out_thread_handle = PlatformThreadHandle(thread_handle, thread_id); else CloseHandle(thread_handle); return true; diff --git a/base/threading/thread.cc b/base/threading/thread.cc index 0e4aab2..bd565c9 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -37,20 +37,6 @@ 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), @@ -72,13 +58,11 @@ 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_(NULL), - thread_id_(kInvalidThreadId), + message_loop_(nullptr), + message_loop_timer_slack_(TIMER_SLACK_NONE), name_(name) { } @@ -104,34 +88,45 @@ bool Thread::StartWithOptions(const Options& options) { SetThreadWasQuitProperly(false); - StartupData startup_data(options); - startup_data_ = &startup_data; + 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)); if (!PlatformThread::Create(options.stack_size, this, &thread_)) { DLOG(ERROR) << "failed to create thread"; - startup_data_ = NULL; + delete message_loop_; + message_loop_ = nullptr; + start_event_.reset(); return false; } - // 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)")); - - // Wait for the thread to start and initialize message_loop_ - base::ThreadRestrictions::ScopedAllowWait allow_wait; - startup_data.event.Wait(); + DCHECK(message_loop_); + return true; +} - // set it to NULL so we don't keep a pointer to some object on the stack. - startup_data_ = NULL; - started_ = true; +bool Thread::StartAndWaitForTesting() { + bool result = Start(); + if (!result) + return false; + WaitUntilThreadStarted(); + return result; +} - DCHECK(message_loop_); +bool Thread::WaitUntilThreadStarted() { + if (!start_event_) + return false; + base::ThreadRestrictions::ScopedAllowWait allow_wait; + start_event_->Wait(); return true; } void Thread::Stop() { - if (!started_) + if (!start_event_) return; StopSoon(); @@ -147,7 +142,7 @@ void Thread::Stop() { DCHECK(!message_loop_); // The thread no longer needs to be joined. - started_ = false; + start_event_.reset(); stopping_ = false; } @@ -155,9 +150,7 @@ void Thread::Stop() { void Thread::StopSoon() { // We should only be called on the same thread that started us. - // 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()); + DCHECK_NE(thread_id(), PlatformThread::CurrentId()); if (stopping_ || !message_loop_) return; @@ -167,13 +160,22 @@ void Thread::StopSoon() { } 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(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_NE(thread_id_, kInvalidThreadId); + DCHECK(message_loop_ != nullptr); PlatformThread::SetThreadPriority(thread_, priority); } @@ -194,60 +196,57 @@ bool Thread::GetThreadWasQuitProperly() { } void Thread::ThreadMain() { - { - // 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)); - } - - // 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(); + // Complete the initialization of our Thread object. + DCHECK_EQ(thread_id(), PlatformThread::CurrentId()); + PlatformThread::SetName(name_.c_str()); + ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. + + // 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_); #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 - // 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(lock_); running_ = true; - startup_data_->event.Signal(); - // startup_data_ can't be touched anymore since the starting thread is now - // unlocked. + } + + start_event_->Signal(); + + Run(message_loop_); - Run(message_loop_); + { + AutoLock lock(lock_); 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. - message_loop_ = NULL; - } + // We can't receive messages anymore. + // (The message loop is destructed at the end of this block) + message_loop_ = NULL; } } // namespace base diff --git a/base/threading/thread.h b/base/threading/thread.h index 4915606..2e19b0a 100644 --- a/base/threading/thread.h +++ b/base/threading/thread.h @@ -13,11 +13,13 @@ #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 @@ -45,7 +47,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { // This is ignored if message_pump_factory.is_null() is false. MessageLoop::Type message_loop_type; - // Specify timer slack for thread message loop. + // Specifies timer slack for thread message loop. TimerSlack timer_slack; // Used to create the MessagePump for the MessageLoop. The callback is Run() @@ -81,7 +83,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(!started_); + DCHECK(!start_event_); com_status_ = use_mta ? MTA : STA; } #endif @@ -103,6 +105,18 @@ 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). @@ -166,7 +180,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { PlatformThreadHandle thread_handle() { return thread_; } // The thread ID. - PlatformThreadId thread_id() const { return thread_id_; } + PlatformThreadId thread_id() const { return thread_.id(); } // Returns true if the thread has been started, and not yet stopped. bool IsRunning() const; @@ -208,19 +222,13 @@ 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_; - - // Used to pass data to ThreadMain. - struct StartupData; - StartupData* startup_data_; + mutable base::Lock lock_; // Protects running_. // The thread's handle. PlatformThreadHandle thread_; @@ -229,12 +237,16 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { // by the created thread. MessageLoop* message_loop_; - // Our thread's ID. - PlatformThreadId thread_id_; + // Stores Options::timer_slack_ until the message loop has been bound to + // a thread. + TimerSlack message_loop_timer_slack_; // 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 b5953d5..b17c681 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.Start(); - thread_b.Start(); + thread_a.StartAndWaitForTesting(); + thread_b.StartAndWaitForTesting(); 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.Start(); + thread_a.StartAndWaitForTesting(); { base::Thread thread_b(kBThread); - thread_b.Start(); + thread_b.StartAndWaitForTesting(); 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.Start(); + thread_a.StartAndWaitForTesting(); base::PlatformThreadId a_id = thread_a.thread_id(); EXPECT_STREQ(kAThread, manager->GetName(a_id)); thread_a.Stop(); - thread_a.Start(); + thread_a.StartAndWaitForTesting(); 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 a89768e..e86c758 100644 --- a/base/threading/thread_unittest.cc +++ b/base/threading/thread_unittest.cc @@ -194,11 +194,12 @@ TEST_F(ThreadTest, ThreadName) { EXPECT_EQ("ThreadName", a.thread_name()); } -// Make sure we can't use a thread between Start() and Init(). +// Make sure Init() is called after Start() and before +// WaitUntilThreadInitialized() returns. TEST_F(ThreadTest, SleepInsideInit) { SleepInsideInitThread t; EXPECT_FALSE(t.InitCalled()); - t.Start(); + t.StartAndWaitForTesting(); EXPECT_TRUE(t.InitCalled()); } diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 874a9ca..80f3bea 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -619,11 +619,6 @@ 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 8ca3443..831e62f 100644 --- a/chrome/browser/io_thread.h +++ b/chrome/browser/io_thread.h @@ -254,7 +254,6 @@ 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_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc index 4b3c457..00171b8 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_->Start(); - io_thread_->Start(); - watchdog_thread_->Start(); + db_thread_->StartAndWaitForTesting(); + io_thread_->StartAndWaitForTesting(); + watchdog_thread_->StartAndWaitForTesting(); WatchDogThread::PostTask( FROM_HERE, @@ -691,7 +691,7 @@ TEST_F(ThreadWatcherListTest, Restart) { content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop_for_ui); scoped_ptr<WatchDogThread> watchdog_thread_(new WatchDogThread()); - watchdog_thread_->Start(); + watchdog_thread_->StartAndWaitForTesting(); // 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 3db65e9..34adb95 100644 --- a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc +++ b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc @@ -336,6 +336,9 @@ 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 78e3836..4ae6607 100644 --- a/content/browser/browser_thread_impl.cc +++ b/content/browser/browser_thread_impl.cc @@ -159,14 +159,8 @@ 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 ad5fd7e..3688e65 100644 --- a/content/public/browser/browser_thread_delegate.h +++ b/content/public/browser/browser_thread_delegate.h @@ -22,9 +22,6 @@ 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 ac67480..62d9eb8 100644 --- a/content/public/test/test_browser_thread.cc +++ b/content/public/test/test_browser_thread.cc @@ -58,6 +58,10 @@ 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 2428bbb..d3a2564 100644 --- a/content/public/test/test_browser_thread.h +++ b/content/public/test/test_browser_thread.h @@ -36,6 +36,10 @@ 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 36ebda6..3224c63 100644 --- a/net/android/network_change_notifier_android.cc +++ b/net/android/network_change_notifier_android.cc @@ -172,6 +172,9 @@ 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(); } |