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::Options::Options() 53 : message_loop_type(MessageLoop::TYPE_DEFAULT), 54 timer_slack(TIMER_SLACK_NONE), 55 stack_size(0) { 56 } 57 58 Thread::Options::Options(MessageLoop::Type type, 59 size_t size) 60 : message_loop_type(type), 61 timer_slack(TIMER_SLACK_NONE), 62 stack_size(size) { 63 } 64 65 Thread::Options::~Options() { 66 } 67 68 Thread::Thread(const std::string& name) 69 : 70 #if defined(OS_WIN) 71 com_status_(NONE), 72 #endif 73 started_(false), 74 stopping_(false), 75 running_(false), 76 startup_data_(NULL), 77 thread_(0), 78 message_loop_(NULL), 79 thread_id_(kInvalidThreadId), 80 name_(name) { 81 } 82 83 Thread::~Thread() { 84 Stop(); 85 } 86 87 bool Thread::Start() { 88 Options options; 89 #if defined(OS_WIN) 90 if (com_status_ == STA) 91 options.message_loop_type = MessageLoop::TYPE_UI; 92 #endif 93 return StartWithOptions(options); 94 } 95 96 bool Thread::StartWithOptions(const Options& options) { 97 DCHECK(!message_loop_); 98 #if defined(OS_WIN) 99 DCHECK((com_status_ != STA) || 100 (options.message_loop_type == MessageLoop::TYPE_UI)); 101 #endif 102 103 SetThreadWasQuitProperly(false); 104 105 StartupData startup_data(options); 106 startup_data_ = &startup_data; 107 108 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 109 DLOG(ERROR) << "failed to create thread"; 110 startup_data_ = NULL; 111 return false; 112 } 113 114 // Wait for the thread to start and initialize message_loop_ 115 base::ThreadRestrictions::ScopedAllowWait allow_wait; 116 startup_data.event.Wait(); 117 118 // set it to NULL so we don't keep a pointer to some object on the stack. 119 startup_data_ = NULL; 120 started_ = true; 121 122 DCHECK(message_loop_); 123 return true; 124 } 125 126 void Thread::Stop() { 127 if (!started_) 128 return; 129 130 StopSoon(); 131 132 // Wait for the thread to exit. 133 // 134 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 135 // the thread exits. Some consumers are abusing the API. Make them stop. 136 // 137 PlatformThread::Join(thread_); 138 139 // The thread should NULL message_loop_ on exit. 140 DCHECK(!message_loop_); 141 142 // The thread no longer needs to be joined. 143 started_ = false; 144 145 stopping_ = false; 146 } 147 148 void Thread::StopSoon() { 149 // We should only be called on the same thread that started us. 150 151 // Reading thread_id_ without a lock can lead to a benign data race 152 // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer. 153 DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId()); 154 155 if (stopping_ || !message_loop_) 156 return; 157 158 stopping_ = true; 159 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); 160 } 161 162 bool Thread::IsRunning() const { 163 return running_; 164 } 165 166 void Thread::SetPriority(ThreadPriority priority) { 167 // The thread must be started (and id known) for this to be 168 // compatible with all platforms. 169 DCHECK_NE(thread_id_, kInvalidThreadId); 170 PlatformThread::SetThreadPriority(thread_, priority); 171 } 172 173 void Thread::Run(MessageLoop* message_loop) { 174 message_loop->Run(); 175 } 176 177 void Thread::SetThreadWasQuitProperly(bool flag) { 178 lazy_tls_bool.Pointer()->Set(flag); 179 } 180 181 bool Thread::GetThreadWasQuitProperly() { 182 bool quit_properly = true; 183 #ifndef NDEBUG 184 quit_properly = lazy_tls_bool.Pointer()->Get(); 185 #endif 186 return quit_properly; 187 } 188 189 void Thread::ThreadMain() { 190 { 191 // The message loop for this thread. 192 // Allocated on the heap to centralize any leak reports at this line. 193 scoped_ptr<MessageLoop> message_loop; 194 if (!startup_data_->options.message_pump_factory.is_null()) { 195 message_loop.reset( 196 new MessageLoop(startup_data_->options.message_pump_factory.Run())); 197 } else { 198 message_loop.reset( 199 new MessageLoop(startup_data_->options.message_loop_type)); 200 } 201 202 // Complete the initialization of our Thread object. 203 thread_id_ = PlatformThread::CurrentId(); 204 PlatformThread::SetName(name_.c_str()); 205 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. 206 message_loop->set_thread_name(name_); 207 message_loop->SetTimerSlack(startup_data_->options.timer_slack); 208 message_loop_ = message_loop.get(); 209 210 #if defined(OS_WIN) 211 scoped_ptr<win::ScopedCOMInitializer> com_initializer; 212 if (com_status_ != NONE) { 213 com_initializer.reset((com_status_ == STA) ? 214 new win::ScopedCOMInitializer() : 215 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); 216 } 217 #endif 218 219 // Let the thread do extra initialization. 220 // Let's do this before signaling we are started. 221 Init(); 222 223 running_ = true; 224 startup_data_->event.Signal(); 225 // startup_data_ can't be touched anymore since the starting thread is now 226 // unlocked. 227 228 Run(message_loop_); 229 running_ = false; 230 231 // Let the thread do extra cleanup. 232 CleanUp(); 233 234 #if defined(OS_WIN) 235 com_initializer.reset(); 236 #endif 237 238 // Assert that MessageLoop::Quit was called by ThreadQuitHelper. 239 DCHECK(GetThreadWasQuitProperly()); 240 241 // We can't receive messages anymore. 242 message_loop_ = NULL; 243 } 244 } 245 246 } // namespace base 247