diff options
Diffstat (limited to 'base/threading/thread.cc')
-rw-r--r-- | base/threading/thread.cc | 206 |
1 files changed, 61 insertions, 145 deletions
diff --git a/base/threading/thread.cc b/base/threading/thread.cc index c30320f0dc..9cdc6912ea 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -5,10 +5,8 @@ #include "base/threading/thread.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/lazy_instance.h" #include "base/location.h" -#include "base/logging.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread_id_name_manager.h" @@ -16,10 +14,6 @@ #include "base/threading/thread_restrictions.h" #include "build/build_config.h" -#if defined(OS_POSIX) && !defined(OS_NACL) -#include "base/files/file_descriptor_watcher_posix.h" -#endif - #if defined(OS_WIN) #include "base/win/scoped_com_initializer.h" #endif @@ -32,31 +26,53 @@ namespace { // because its Stop method was called. This allows us to catch cases where // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when // using a Thread to setup and run a MessageLoop. -base::LazyInstance<base::ThreadLocalBoolean>::Leaky lazy_tls_bool = +base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool = LAZY_INSTANCE_INITIALIZER; } // namespace -Thread::Options::Options() = default; +// This is used to trigger the message loop to exit. +void ThreadQuitHelper() { + MessageLoop::current()->QuitWhenIdle(); + Thread::SetThreadWasQuitProperly(true); +} + +Thread::Options::Options() + : message_loop_type(MessageLoop::TYPE_DEFAULT), + timer_slack(TIMER_SLACK_NONE), + stack_size(0), + priority(ThreadPriority::NORMAL) { +} -Thread::Options::Options(MessageLoop::Type type, size_t size) - : message_loop_type(type), stack_size(size) {} +Thread::Options::Options(MessageLoop::Type type, + size_t size) + : message_loop_type(type), + timer_slack(TIMER_SLACK_NONE), + stack_size(size), + priority(ThreadPriority::NORMAL) { +} Thread::Options::Options(const Options& other) = default; -Thread::Options::~Options() = default; +Thread::Options::~Options() { +} Thread::Thread(const std::string& name) - : id_event_(WaitableEvent::ResetPolicy::MANUAL, + : +#if defined(OS_WIN) + com_status_(NONE), +#endif + stopping_(false), + running_(false), + thread_(0), + id_(kInvalidThreadId), + id_event_(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED), + message_loop_(nullptr), + message_loop_timer_slack_(TIMER_SLACK_NONE), name_(name), start_event_(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED) { - // Only bind the sequence on Start(): the state is constant between - // construction and Start() and it's thus valid for Start() to be called on - // another sequence as long as every other operation is then performed on that - // sequence. - owning_sequence_checker_.DetachFromSequence(); } Thread::~Thread() { @@ -64,8 +80,6 @@ Thread::~Thread() { } bool Thread::Start() { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - Options options; #if defined(OS_WIN) if (com_status_ == STA) @@ -75,11 +89,7 @@ bool Thread::Start() { } bool Thread::StartWithOptions(const Options& options) { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); DCHECK(!message_loop_); - DCHECK(!IsRunning()); - DCHECK(!stopping_) << "Starting a non-joinable thread a second time? That's " - << "not allowed!"; #if defined(OS_WIN) DCHECK((com_status_ != STA) || (options.message_loop_type == MessageLoop::TYPE_UI)); @@ -96,41 +106,32 @@ bool Thread::StartWithOptions(const Options& options) { type = MessageLoop::TYPE_CUSTOM; message_loop_timer_slack_ = options.timer_slack; - std::unique_ptr<MessageLoop> message_loop_owned = + std::unique_ptr<MessageLoop> message_loop = MessageLoop::CreateUnbound(type, options.message_pump_factory); - message_loop_ = message_loop_owned.get(); + message_loop_ = message_loop.get(); start_event_.Reset(); - // Hold |thread_lock_| while starting the new thread to synchronize with - // Stop() while it's not guaranteed to be sequenced (until crbug/629139 is - // fixed). + // 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_); - bool success = - options.joinable - ? PlatformThread::CreateWithPriority(options.stack_size, this, - &thread_, options.priority) - : PlatformThread::CreateNonJoinableWithPriority( - options.stack_size, this, options.priority); - if (!success) { + if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, + options.priority)) { DLOG(ERROR) << "failed to create thread"; message_loop_ = nullptr; return false; } } - joinable_ = options.joinable; - - // The ownership of |message_loop_| is managed by the newly created thread + // The ownership of message_loop is managemed by the newly created thread // within the ThreadMain. - ignore_result(message_loop_owned.release()); + ignore_result(message_loop.release()); DCHECK(message_loop_); return true; } bool Thread::StartAndWaitForTesting() { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); bool result = Start(); if (!result) return false; @@ -139,7 +140,6 @@ bool Thread::StartAndWaitForTesting() { } bool Thread::WaitUntilThreadStarted() const { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); if (!message_loop_) return false; base::ThreadRestrictions::ScopedAllowWait allow_wait; @@ -147,74 +147,37 @@ bool Thread::WaitUntilThreadStarted() const { return true; } -void Thread::FlushForTesting() { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - if (!message_loop_) - return; - - WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask(FROM_HERE, - Bind(&WaitableEvent::Signal, Unretained(&done))); - done.Wait(); -} - void Thread::Stop() { - DCHECK(joinable_); - - // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and - // enable this check, until then synchronization with Start() via - // |thread_lock_| is required... - // DCHECK(owning_sequence_checker_.CalledOnValidSequence()); AutoLock lock(thread_lock_); - - StopSoon(); - - // Can't join if the |thread_| is either already gone or is non-joinable. if (thread_.is_null()) return; + StopSoon(); + // Wait for the thread to exit. // - // TODO(darin): Unfortunately, we need to keep |message_loop_| around until + // TODO(darin): Unfortunately, we need to keep message_loop_ around until // the thread exits. Some consumers are abusing the API. Make them stop. // PlatformThread::Join(thread_); thread_ = base::PlatformThreadHandle(); - // The thread should nullify |message_loop_| on exit (note: Join() adds an - // implicit memory barrier and no lock is thus required for this check). + // The thread should nullify message_loop_ on exit. DCHECK(!message_loop_); stopping_ = false; } void Thread::StopSoon() { - // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and - // enable this check. - // DCHECK(owning_sequence_checker_.CalledOnValidSequence()); + // We should only be called on the same thread that started us. + + DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); if (stopping_ || !message_loop_) return; stopping_ = true; - - if (using_external_message_loop_) { - // Setting |stopping_| to true above should have been sufficient for this - // thread to be considered "stopped" per it having never set its |running_| - // bit by lack of its own ThreadMain. - DCHECK(!IsRunning()); - message_loop_ = nullptr; - return; - } - - task_runner()->PostTask( - FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this))); -} - -void Thread::DetachFromSequence() { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - owning_sequence_checker_.DetachFromSequence(); + task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); } PlatformThreadId Thread::GetThreadId() const { @@ -225,36 +188,26 @@ PlatformThreadId Thread::GetThreadId() const { } bool Thread::IsRunning() const { - // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and - // enable this check. - // DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - - // 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 sequence that starts / - // started the new thread so we need no locking here.) + // 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 + // 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::Run(RunLoop* run_loop) { - // Overridable protected method to be called from our |thread_| only. - DCHECK(id_event_.IsSignaled()); - DCHECK_EQ(id_, PlatformThread::CurrentId()); - - run_loop->Run(); +void Thread::Run(MessageLoop*) { + RunLoop().Run(); } -// static void Thread::SetThreadWasQuitProperly(bool flag) { lazy_tls_bool.Pointer()->Set(flag); } -// static bool Thread::GetThreadWasQuitProperly() { bool quit_properly = true; #ifndef NDEBUG @@ -263,27 +216,9 @@ bool Thread::GetThreadWasQuitProperly() { return quit_properly; } -void Thread::SetMessageLoop(MessageLoop* message_loop) { - DCHECK(owning_sequence_checker_.CalledOnValidSequence()); - DCHECK(message_loop); - - // Setting |message_loop_| should suffice for this thread to be considered - // as "running", until Stop() is invoked. - DCHECK(!IsRunning()); - message_loop_ = message_loop; - DCHECK(IsRunning()); - - using_external_message_loop_ = true; -} - void Thread::ThreadMain() { // First, make GetThreadId() available to avoid deadlocks. It could be called // any place in the following thread initialization code. - DCHECK(!id_event_.IsSignaled()); - // Note: this read of |id_| while |id_event_| isn't signaled is exceptionally - // okay because ThreadMain has a happens-after relationship with the other - // write in StartWithOptions(). - DCHECK_EQ(kInvalidThreadId, id_); id_ = PlatformThread::CurrentId(); DCHECK_NE(kInvalidThreadId, id_); id_event_.Signal(); @@ -291,22 +226,12 @@ void Thread::ThreadMain() { // Complete the initialization of our Thread object. PlatformThread::SetName(name_.c_str()); - // Lazily initialize the |message_loop| so that it can run on this thread. + // Lazily initialize the message_loop so that it can run on this thread. DCHECK(message_loop_); std::unique_ptr<MessageLoop> message_loop(message_loop_); message_loop_->BindToCurrentThread(); message_loop_->SetTimerSlack(message_loop_timer_slack_); -#if defined(OS_POSIX) && !defined(OS_NACL) - // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API. - std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher; - if (MessageLoopForIO::IsCurrent()) { - DCHECK_EQ(message_loop_, MessageLoopForIO::current()); - file_descriptor_watcher.reset( - new FileDescriptorWatcher(MessageLoopForIO::current())); - } -#endif - #if defined(OS_WIN) std::unique_ptr<win::ScopedCOMInitializer> com_initializer; if (com_status_ != NONE) { @@ -326,9 +251,7 @@ void Thread::ThreadMain() { start_event_.Signal(); - RunLoop run_loop; - run_loop_ = &run_loop; - Run(run_loop_); + Run(message_loop_); { AutoLock lock(running_lock_); @@ -343,22 +266,15 @@ void Thread::ThreadMain() { #endif if (message_loop->type() != MessageLoop::TYPE_CUSTOM) { - // Assert that RunLoop::QuitWhenIdle was called by ThreadQuitHelper. Don't - // check for custom message pumps, because their shutdown might not allow - // this. + // Assert that MessageLoop::QuitWhenIdle was called by ThreadQuitHelper. + // Don't check for custom message pumps, because their shutdown might not + // allow this. DCHECK(GetThreadWasQuitProperly()); } // We can't receive messages anymore. // (The message loop is destructed at the end of this block) message_loop_ = nullptr; - run_loop_ = nullptr; -} - -void Thread::ThreadQuitHelper() { - DCHECK(run_loop_); - run_loop_->QuitWhenIdle(); - SetThreadWasQuitProperly(true); } } // namespace base |