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