| // Copyright 2012 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/threading/thread.h" | |
| #include <memory> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "base/dcheck_is_on.h" | |
| #include "base/functional/bind.h" | |
| #include "base/functional/callback_helpers.h" | |
| #include "base/location.h" | |
| #include "base/logging.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/memory/scoped_refptr.h" | |
| #include "base/message_loop/message_pump.h" | |
| #include "base/run_loop.h" | |
| #include "base/synchronization/waitable_event.h" | |
| #include "base/task/current_thread.h" | |
| #include "base/task/sequence_manager/sequence_manager_impl.h" | |
| #include "base/task/sequence_manager/task_queue.h" | |
| #include "base/task/single_thread_task_runner.h" | |
| #include "base/threading/thread_id_name_manager.h" | |
| #include "base/threading/thread_restrictions.h" | |
| #include "base/types/pass_key.h" | |
| #include "build/build_config.h" | |
| #include "third_party/abseil-cpp/absl/base/dynamic_annotations.h" | |
| #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | |
| #include <optional> | |
| #include "base/files/file_descriptor_watcher_posix.h" | |
| #endif | |
| #if BUILDFLAG(IS_WIN) | |
| #include "base/win/scoped_com_initializer.h" | |
| #endif | |
| namespace base { | |
| #if DCHECK_IS_ON() | |
| namespace { | |
| // We use this thread-local variable to record whether or not a thread exited | |
| // 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. | |
| constinit thread_local bool was_quit_properly = false; | |
| } // namespace | |
| #endif | |
| namespace internal { | |
| class SequenceManagerThreadDelegate : public Thread::Delegate { | |
| public: | |
| explicit SequenceManagerThreadDelegate( | |
| MessagePumpType message_pump_type, | |
| OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory, | |
| std::unique_ptr<base::sequence_manager::SequenceManagerSettings> | |
| sequence_manager_settings) | |
| : sequence_manager_( | |
| sequence_manager::internal::CreateUnboundSequenceManagerImpl( | |
| PassKey<base::internal::SequenceManagerThreadDelegate>(), | |
| sequence_manager_settings | |
| ? std::move(sequence_manager_settings->settings) | |
| : sequence_manager::SequenceManager::Settings::Builder() | |
| .SetMessagePumpType(message_pump_type) | |
| .Build())), | |
| default_task_queue_(sequence_manager_->CreateTaskQueue( | |
| sequence_manager::TaskQueue::Spec( | |
| sequence_manager::QueueName::DEFAULT_TQ))), | |
| message_pump_factory_(std::move(message_pump_factory)) { | |
| sequence_manager_->SetDefaultTaskQueue(default_task_queue_.get()); | |
| } | |
| ~SequenceManagerThreadDelegate() override = default; | |
| scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override { | |
| // Surprisingly this might not be default_task_queue_->task_runner() which | |
| // we set in the constructor. The Thread::Init() method could create a | |
| // SequenceManager on top of the current one and call | |
| // SequenceManager::SetDefaultTaskRunner which would propagate the new | |
| // TaskRunner down to our SequenceManager. Turns out, code actually relies | |
| // on this and somehow relies on | |
| // SequenceManagerThreadDelegate::GetDefaultTaskRunner returning this new | |
| // TaskRunner. So instead of returning default_task_queue_->task_runner() we | |
| // need to query the SequenceManager for it. | |
| // The underlying problem here is that Subclasses of Thread can do crazy | |
| // stuff in Init() but they are not really in control of what happens in the | |
| // Thread::Delegate, as this is passed in on calling StartWithOptions which | |
| // could happen far away from where the Thread is created. We should | |
| // consider getting rid of StartWithOptions, and pass them as a constructor | |
| // argument instead. | |
| return sequence_manager_->GetDefaultTaskRunner(); | |
| } | |
| void BindToCurrentThread() override { | |
| sequence_manager_->BindToMessagePump( | |
| std::move(message_pump_factory_).Run()); | |
| } | |
| private: | |
| std::unique_ptr<sequence_manager::internal::SequenceManagerImpl> | |
| sequence_manager_; | |
| sequence_manager::TaskQueue::Handle default_task_queue_; | |
| OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory_; | |
| }; | |
| } // namespace internal | |
| Thread::Options::Options() = default; | |
| Thread::Options::Options(MessagePumpType type, size_t size) | |
| : message_pump_type(type), stack_size(size) {} | |
| Thread::Options::Options(ThreadType thread_type) : thread_type(thread_type) {} | |
| Thread::Options::Options(Options&& other) | |
| : message_pump_type(std::move(other.message_pump_type)), | |
| message_pump_factory(std::move(other.message_pump_factory)), | |
| stack_size(std::move(other.stack_size)), | |
| thread_type(std::move(other.thread_type)), | |
| joinable(std::move(other.joinable)), | |
| sequence_manager_settings(std::move(other.sequence_manager_settings)) { | |
| other.moved_from = true; | |
| } | |
| Thread::Options& Thread::Options::operator=(Thread::Options&& other) { | |
| DCHECK_NE(this, &other); | |
| message_pump_type = std::move(other.message_pump_type); | |
| message_pump_factory = std::move(other.message_pump_factory); | |
| stack_size = std::move(other.stack_size); | |
| thread_type = std::move(other.thread_type); | |
| joinable = std::move(other.joinable); | |
| sequence_manager_settings = std::move(other.sequence_manager_settings); | |
| other.moved_from = true; | |
| return *this; | |
| } | |
| Thread::Options::~Options() = default; | |
| Thread::Thread(const std::string& name, Restartable) | |
| : Thread(std::move(name), nullptr, true) {} | |
| Thread::Thread(const std::string& name, std::unique_ptr<Delegate> delegate) | |
| : Thread(std::move(name), std::move(delegate), false) {} | |
| Thread::Thread(const std::string& name, | |
| std::unique_ptr<Delegate> delegate, | |
| bool restartable) | |
| : restartable_(restartable), | |
| id_event_(WaitableEvent::ResetPolicy::MANUAL, | |
| WaitableEvent::InitialState::NOT_SIGNALED), | |
| delegate_(std::move(delegate)), | |
| name_(name), | |
| start_event_(WaitableEvent::ResetPolicy::MANUAL, | |
| WaitableEvent::InitialState::NOT_SIGNALED) { | |
| DCHECK(!delegate_ || !restartable_); | |
| // 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() { | |
| Stop(); | |
| } | |
| bool Thread::Start() { | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| Options options; | |
| #if BUILDFLAG(IS_WIN) | |
| if (com_status_ == STA) { | |
| options.message_pump_type = MessagePumpType::UI; | |
| } | |
| #endif | |
| return StartWithOptions(std::move(options)); | |
| } | |
| bool Thread::StartWithOptions(Options options) { | |
| DCHECK(options.IsValid()); | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| DCHECK_NE(state_, State::kStopping) | |
| << "Can't restart a thread which wasn't fully Stop()'ed."; | |
| DCHECK_NE(state_, State::kRunning) | |
| << "Trying to start a thread that's already running."; | |
| DCHECK(state_ == State::kInitial || restartable_) | |
| << "Trying to restart a thread that doesn't support restarting."; | |
| #if BUILDFLAG(IS_WIN) | |
| DCHECK((com_status_ != STA) || | |
| (options.message_pump_type == MessagePumpType::UI)); | |
| #endif | |
| // Reset |id_| here to support restarting the thread. | |
| id_event_.Reset(); | |
| id_ = kInvalidThreadId; | |
| SetThreadWasQuitProperly(false); | |
| DCHECK(!(delegate_ && options.message_pump_factory)); | |
| if (!delegate_) { | |
| if (options.message_pump_factory) { | |
| delegate_ = std::make_unique<internal::SequenceManagerThreadDelegate>( | |
| MessagePumpType::CUSTOM, options.message_pump_factory, | |
| std::move(options.sequence_manager_settings)); | |
| } else { | |
| delegate_ = std::make_unique<internal::SequenceManagerThreadDelegate>( | |
| options.message_pump_type, | |
| BindOnce( | |
| [](MessagePumpType type) { return MessagePump::Create(type); }, | |
| options.message_pump_type), | |
| std::move(options.sequence_manager_settings)); | |
| } | |
| } | |
| start_event_.Reset(); | |
| state_ = State::kRunning; | |
| // 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). | |
| { | |
| AutoLock lock(thread_lock_); | |
| bool success = options.joinable | |
| ? PlatformThread::CreateWithType( | |
| options.stack_size, this, &thread_, | |
| options.thread_type, options.message_pump_type) | |
| : PlatformThread::CreateNonJoinableWithType( | |
| options.stack_size, this, options.thread_type, | |
| options.message_pump_type); | |
| if (!success) { | |
| DLOG(ERROR) << "failed to create thread"; | |
| return false; | |
| } | |
| } | |
| joinable_ = options.joinable; | |
| return true; | |
| } | |
| bool Thread::StartAndWaitForTesting() { | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| bool result = Start(); | |
| if (!result) { | |
| return false; | |
| } | |
| WaitUntilThreadStarted(); | |
| return true; | |
| } | |
| bool Thread::WaitUntilThreadStarted() const { | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| if (!delegate_) { | |
| return false; | |
| } | |
| // https://crbug.com/918039 | |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; | |
| start_event_.Wait(); | |
| return true; | |
| } | |
| void Thread::FlushForTesting() { | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| if (!delegate_) { | |
| return; | |
| } | |
| WaitableEvent done(WaitableEvent::ResetPolicy::AUTOMATIC, | |
| WaitableEvent::InitialState::NOT_SIGNALED); | |
| task_runner()->PostTask(FROM_HERE, | |
| BindOnce(&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; | |
| } | |
| // Wait for the thread to exit. | |
| // | |
| // TODO(darin): Unfortunately, we need to keep |delegate_| around | |
| // until the thread exits. Some consumers are abusing the API. Make them stop. | |
| PlatformThread::Join(thread_); | |
| thread_ = base::PlatformThreadHandle(); | |
| // The thread should release |delegate_| on exit (note: Join() adds | |
| // an implicit memory barrier and no lock is thus required for this check). | |
| DCHECK(!delegate_); | |
| state_ = State::kStopped; | |
| } | |
| void Thread::StopSoon() { | |
| // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and | |
| // enable this check. | |
| // DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| if (state_ != State::kRunning) { | |
| return; | |
| } | |
| state_ = State::kStopping; | |
| task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce(&Thread::ThreadQuitHelper, Unretained(this))); | |
| } | |
| void Thread::DetachFromSequence() { | |
| DCHECK(owning_sequence_checker_.CalledOnValidSequence()); | |
| owning_sequence_checker_.DetachFromSequence(); | |
| } | |
| PlatformThreadId Thread::GetThreadId() const { | |
| if (!id_event_.IsSignaled()) { | |
| // If the thread is created but not started yet, wait for |id_| being ready. | |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; | |
| id_event_.Wait(); | |
| } | |
| return id_; | |
| } | |
| 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()); | |
| // Note that |state_| is touched only on the same sequence that | |
| // starts / started the new thread so we need no locking here. | |
| return state_ == State::kRunning || state_ == State::kStopping; | |
| } | |
| 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(); | |
| } | |
| // static | |
| void Thread::SetThreadWasQuitProperly(bool flag) { | |
| #if DCHECK_IS_ON() | |
| was_quit_properly = flag; | |
| #endif | |
| } | |
| // static | |
| bool Thread::GetThreadWasQuitProperly() { | |
| #if DCHECK_IS_ON() | |
| return was_quit_properly; | |
| #else | |
| return true; | |
| #endif | |
| } | |
| void Thread::ThreadMain() { | |
| // First, set the thread name. It is important to do this first because some | |
| // of the code below may end up storing/caching the thread name. One example | |
| // is Perfetto being triggered by a TRACE_EVENT call from id_event_.Signal(). | |
| // See https://crbug.com/333597498. | |
| PlatformThread::SetName(name_.c_str()); | |
| ABSL_ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. | |
| // 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(); | |
| // Lazily initialize the |message_loop| so that it can run on this thread. | |
| DCHECK(delegate_); | |
| // This binds CurrentThread and SingleThreadTaskRunner::CurrentDefaultHandle. | |
| delegate_->BindToCurrentThread(); | |
| DCHECK(CurrentThread::Get()); | |
| DCHECK(SingleThreadTaskRunner::HasCurrentDefault()); | |
| #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | |
| // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API. | |
| std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher; | |
| if (CurrentIOThread::IsSet()) { | |
| file_descriptor_watcher = std::make_unique<FileDescriptorWatcher>( | |
| delegate_->GetDefaultTaskRunner()); | |
| } | |
| #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | |
| #if BUILDFLAG(IS_WIN) | |
| std::unique_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. | |
| Init(); | |
| start_event_.Signal(); | |
| RunLoop run_loop; | |
| run_loop_ = &run_loop; | |
| Run(run_loop_); | |
| // Let the thread do extra cleanup. | |
| CleanUp(); | |
| #if BUILDFLAG(IS_WIN) | |
| com_initializer.reset(); | |
| #endif | |
| DCHECK(GetThreadWasQuitProperly()); | |
| // We can't receive messages anymore. | |
| // (The message loop is destructed at the end of this block) | |
| delegate_.reset(); | |
| run_loop_ = nullptr; | |
| } | |
| void Thread::ThreadQuitHelper() { | |
| DCHECK(run_loop_); | |
| run_loop_->QuitWhenIdle(); | |
| SetThreadWasQuitProperly(true); | |
| } | |
| } // namespace base |