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