1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/threading/thread.h" 6 7 #include "base/bind.h" 8 #include "base/lazy_instance.h" 9 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 10 #include "base/threading/thread_id_name_manager.h" 11 #include "base/threading/thread_local.h" 12 #include "base/threading/thread_restrictions.h" 13 #include "base/synchronization/waitable_event.h" 14 15 #if defined(OS_WIN) 16 #include "base/win/scoped_com_initializer.h" 17 #endif 18 19 namespace base { 20 21 namespace { 22 23 // We use this thread-local variable to record whether or not a thread exited 24 // because its Stop method was called. This allows us to catch cases where 25 // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when 26 // using a Thread to setup and run a MessageLoop. 27 base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool = 28 LAZY_INSTANCE_INITIALIZER; 29 30 } // namespace 31 32 // This is used to trigger the message loop to exit. 33 void ThreadQuitHelper() { 34 MessageLoop::current()->QuitWhenIdle(); 35 Thread::SetThreadWasQuitProperly(true); 36 } 37 38 // Used to pass data to ThreadMain. This structure is allocated on the stack 39 // from within StartWithOptions. 40 struct Thread::StartupData { 41 // We get away with a const reference here because of how we are allocated. 42 const Thread::Options& options; 43 44 // Used to synchronize thread startup. 45 WaitableEvent event; 46 47 explicit StartupData(const Options& opt) 48 : options(opt), 49 event(false, false) {} 50 }; 51 52 Thread::Thread(const char* name) 53 : 54 #if defined(OS_WIN) 55 com_status_(NONE), 56 #endif 57 started_(false), 58 stopping_(false), 59 running_(false), 60 startup_data_(NULL), 61 thread_(0), 62 message_loop_(NULL), 63 thread_id_(kInvalidThreadId), 64 name_(name) { 65 } 66 67 Thread::~Thread() { 68 Stop(); 69 } 70 71 bool Thread::Start() { 72 Options options; 73 #if defined(OS_WIN) 74 if (com_status_ == STA) 75 options.message_loop_type = MessageLoop::TYPE_UI; 76 #endif 77 return StartWithOptions(options); 78 } 79 80 bool Thread::StartWithOptions(const Options& options) { 81 DCHECK(!message_loop_); 82 #if defined(OS_WIN) 83 DCHECK((com_status_ != STA) || 84 (options.message_loop_type == MessageLoop::TYPE_UI)); 85 #endif 86 87 SetThreadWasQuitProperly(false); 88 89 StartupData startup_data(options); 90 startup_data_ = &startup_data; 91 92 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 93 DLOG(ERROR) << "failed to create thread"; 94 startup_data_ = NULL; 95 return false; 96 } 97 98 // Wait for the thread to start and initialize message_loop_ 99 base::ThreadRestrictions::ScopedAllowWait allow_wait; 100 startup_data.event.Wait(); 101 102 // set it to NULL so we don't keep a pointer to some object on the stack. 103 startup_data_ = NULL; 104 started_ = true; 105 106 DCHECK(message_loop_); 107 return true; 108 } 109 110 void Thread::Stop() { 111 if (!started_) 112 return; 113 114 StopSoon(); 115 116 // Wait for the thread to exit. 117 // 118 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 119 // the thread exits. Some consumers are abusing the API. Make them stop. 120 // 121 PlatformThread::Join(thread_); 122 123 // The thread should NULL message_loop_ on exit. 124 DCHECK(!message_loop_); 125 126 // The thread no longer needs to be joined. 127 started_ = false; 128 129 stopping_ = false; 130 } 131 132 void Thread::StopSoon() { 133 // We should only be called on the same thread that started us. 134 135 // Reading thread_id_ without a lock can lead to a benign data race 136 // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer. 137 DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId()); 138 139 if (stopping_ || !message_loop_) 140 return; 141 142 stopping_ = true; 143 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); 144 } 145 146 bool Thread::IsRunning() const { 147 return running_; 148 } 149 150 void Thread::SetPriority(ThreadPriority priority) { 151 // The thread must be started (and id known) for this to be 152 // compatible with all platforms. 153 DCHECK_NE(thread_id_, kInvalidThreadId); 154 PlatformThread::SetThreadPriority(thread_, priority); 155 } 156 157 void Thread::Run(MessageLoop* message_loop) { 158 message_loop->Run(); 159 } 160 161 void Thread::SetThreadWasQuitProperly(bool flag) { 162 lazy_tls_bool.Pointer()->Set(flag); 163 } 164 165 bool Thread::GetThreadWasQuitProperly() { 166 bool quit_properly = true; 167 #ifndef NDEBUG 168 quit_properly = lazy_tls_bool.Pointer()->Get(); 169 #endif 170 return quit_properly; 171 } 172 173 void Thread::ThreadMain() { 174 { 175 // The message loop for this thread. 176 MessageLoop message_loop(startup_data_->options.message_loop_type); 177 178 // Complete the initialization of our Thread object. 179 thread_id_ = PlatformThread::CurrentId(); 180 PlatformThread::SetName(name_.c_str()); 181 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. 182 message_loop.set_thread_name(name_); 183 message_loop_ = &message_loop; 184 185 #if defined(OS_WIN) 186 scoped_ptr<win::ScopedCOMInitializer> com_initializer; 187 if (com_status_ != NONE) { 188 com_initializer.reset((com_status_ == STA) ? 189 new win::ScopedCOMInitializer() : 190 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); 191 } 192 #endif 193 194 // Let the thread do extra initialization. 195 // Let's do this before signaling we are started. 196 Init(); 197 198 running_ = true; 199 startup_data_->event.Signal(); 200 // startup_data_ can't be touched anymore since the starting thread is now 201 // unlocked. 202 203 Run(message_loop_); 204 running_ = false; 205 206 // Let the thread do extra cleanup. 207 CleanUp(); 208 209 #if defined(OS_WIN) 210 com_initializer.reset(); 211 #endif 212 213 // Assert that MessageLoop::Quit was called by ThreadQuitHelper. 214 DCHECK(GetThreadWasQuitProperly()); 215 216 // We can't receive messages anymore. 217 message_loop_ = NULL; 218 } 219 } 220 221 } // namespace base 222