|  | // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/threading/simple_thread.h" | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/thread_restrictions.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | SimpleThread::SimpleThread(const std::string& name_prefix) | 
|  | : SimpleThread(name_prefix, Options()) {} | 
|  |  | 
|  | SimpleThread::SimpleThread(const std::string& name_prefix, | 
|  | const Options& options) | 
|  | : name_prefix_(name_prefix), | 
|  | options_(options), | 
|  | event_(WaitableEvent::ResetPolicy::MANUAL, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED) {} | 
|  |  | 
|  | SimpleThread::~SimpleThread() { | 
|  | DCHECK(HasBeenStarted()) << "SimpleThread was never started."; | 
|  | DCHECK(!options_.joinable || HasBeenJoined()) | 
|  | << "Joinable SimpleThread destroyed without being Join()ed."; | 
|  | } | 
|  |  | 
|  | void SimpleThread::Start() { | 
|  | StartAsync(); | 
|  | ThreadRestrictions::ScopedAllowWait allow_wait; | 
|  | event_.Wait();  // Wait for the thread to complete initialization. | 
|  | } | 
|  |  | 
|  | void SimpleThread::Join() { | 
|  | DCHECK(options_.joinable) << "A non-joinable thread can't be joined."; | 
|  | DCHECK(HasStartBeenAttempted()) << "Tried to Join a never-started thread."; | 
|  | DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times."; | 
|  | BeforeJoin(); | 
|  | PlatformThread::Join(thread_); | 
|  | thread_ = PlatformThreadHandle(); | 
|  | joined_ = true; | 
|  | } | 
|  |  | 
|  | void SimpleThread::StartAsync() { | 
|  | DCHECK(!HasStartBeenAttempted()) << "Tried to Start a thread multiple times."; | 
|  | start_called_ = true; | 
|  | BeforeStart(); | 
|  | bool success = | 
|  | options_.joinable | 
|  | ? PlatformThread::CreateWithPriority(options_.stack_size, this, | 
|  | &thread_, options_.priority) | 
|  | : PlatformThread::CreateNonJoinableWithPriority( | 
|  | options_.stack_size, this, options_.priority); | 
|  | DCHECK(success); | 
|  | } | 
|  |  | 
|  | PlatformThreadId SimpleThread::tid() { | 
|  | DCHECK(HasBeenStarted()); | 
|  | return tid_; | 
|  | } | 
|  |  | 
|  | bool SimpleThread::HasBeenStarted() { | 
|  | ThreadRestrictions::ScopedAllowWait allow_wait; | 
|  | return event_.IsSignaled(); | 
|  | } | 
|  |  | 
|  | void SimpleThread::ThreadMain() { | 
|  | tid_ = PlatformThread::CurrentId(); | 
|  | // Construct our full name of the form "name_prefix_/TID". | 
|  | std::string name(name_prefix_); | 
|  | name.push_back('/'); | 
|  | name.append(IntToString(tid_)); | 
|  | PlatformThread::SetName(name); | 
|  |  | 
|  | // We've initialized our new thread, signal that we're done to Start(). | 
|  | event_.Signal(); | 
|  |  | 
|  | BeforeRun(); | 
|  | Run(); | 
|  | } | 
|  |  | 
|  | DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate, | 
|  | const std::string& name_prefix) | 
|  | : DelegateSimpleThread(delegate, name_prefix, Options()) {} | 
|  |  | 
|  | DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate, | 
|  | const std::string& name_prefix, | 
|  | const Options& options) | 
|  | : SimpleThread(name_prefix, options), | 
|  | delegate_(delegate) { | 
|  | DCHECK(delegate_); | 
|  | } | 
|  |  | 
|  | DelegateSimpleThread::~DelegateSimpleThread() = default; | 
|  |  | 
|  | void DelegateSimpleThread::Run() { | 
|  | DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)"; | 
|  |  | 
|  | // Non-joinable DelegateSimpleThreads are allowed to be deleted during Run(). | 
|  | // Member state must not be accessed after invoking Run(). | 
|  | Delegate* delegate = delegate_; | 
|  | delegate_ = nullptr; | 
|  | delegate->Run(); | 
|  | } | 
|  |  | 
|  | DelegateSimpleThreadPool::DelegateSimpleThreadPool( | 
|  | const std::string& name_prefix, | 
|  | int num_threads) | 
|  | : name_prefix_(name_prefix), | 
|  | num_threads_(num_threads), | 
|  | dry_(WaitableEvent::ResetPolicy::MANUAL, | 
|  | WaitableEvent::InitialState::NOT_SIGNALED) {} | 
|  |  | 
|  | DelegateSimpleThreadPool::~DelegateSimpleThreadPool() { | 
|  | DCHECK(threads_.empty()); | 
|  | DCHECK(delegates_.empty()); | 
|  | DCHECK(!dry_.IsSignaled()); | 
|  | } | 
|  |  | 
|  | void DelegateSimpleThreadPool::Start() { | 
|  | DCHECK(threads_.empty()) << "Start() called with outstanding threads."; | 
|  | for (int i = 0; i < num_threads_; ++i) { | 
|  | DelegateSimpleThread* thread = new DelegateSimpleThread(this, name_prefix_); | 
|  | thread->Start(); | 
|  | threads_.push_back(thread); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DelegateSimpleThreadPool::JoinAll() { | 
|  | DCHECK(!threads_.empty()) << "JoinAll() called with no outstanding threads."; | 
|  |  | 
|  | // Tell all our threads to quit their worker loop. | 
|  | AddWork(nullptr, num_threads_); | 
|  |  | 
|  | // Join and destroy all the worker threads. | 
|  | for (int i = 0; i < num_threads_; ++i) { | 
|  | threads_[i]->Join(); | 
|  | delete threads_[i]; | 
|  | } | 
|  | threads_.clear(); | 
|  | DCHECK(delegates_.empty()); | 
|  | } | 
|  |  | 
|  | void DelegateSimpleThreadPool::AddWork(Delegate* delegate, int repeat_count) { | 
|  | AutoLock locked(lock_); | 
|  | for (int i = 0; i < repeat_count; ++i) | 
|  | delegates_.push(delegate); | 
|  | // If we were empty, signal that we have work now. | 
|  | if (!dry_.IsSignaled()) | 
|  | dry_.Signal(); | 
|  | } | 
|  |  | 
|  | void DelegateSimpleThreadPool::Run() { | 
|  | Delegate* work = nullptr; | 
|  |  | 
|  | while (true) { | 
|  | dry_.Wait(); | 
|  | { | 
|  | AutoLock locked(lock_); | 
|  | if (!dry_.IsSignaled()) | 
|  | continue; | 
|  |  | 
|  | DCHECK(!delegates_.empty()); | 
|  | work = delegates_.front(); | 
|  | delegates_.pop(); | 
|  |  | 
|  | // Signal to any other threads that we're currently out of work. | 
|  | if (delegates_.empty()) | 
|  | dry_.Reset(); | 
|  | } | 
|  |  | 
|  | // A NULL delegate pointer signals us to quit. | 
|  | if (!work) | 
|  | break; | 
|  |  | 
|  | work->Run(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace base |