thread.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 19namespace base { 20 21namespace { 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. 27base::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. 33void 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. 40struct 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 52Thread::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 67Thread::~Thread() { 68 Stop(); 69} 70 71bool 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 80bool 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 110void 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 132void 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 146bool Thread::IsRunning() const { 147 return running_; 148} 149 150void 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 157void Thread::Run(MessageLoop* message_loop) { 158 message_loop->Run(); 159} 160 161void Thread::SetThreadWasQuitProperly(bool flag) { 162 lazy_tls_bool.Pointer()->Set(flag); 163} 164 165bool 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 173void 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