• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ThreadQuitHelper()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 
StartupDatabase::Thread::StartupData47   explicit StartupData(const Options& opt)
48       : options(opt),
49         event(false, false) {}
50 };
51 
Options()52 Thread::Options::Options()
53     : message_loop_type(MessageLoop::TYPE_DEFAULT),
54       timer_slack(TIMER_SLACK_NONE),
55       stack_size(0) {
56 }
57 
Options(MessageLoop::Type type,size_t size)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 
~Options()65 Thread::Options::~Options() {
66 }
67 
Thread(const std::string & name)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 
~Thread()83 Thread::~Thread() {
84   Stop();
85 }
86 
Start()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 
StartWithOptions(const Options & options)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 
Stop()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 
StopSoon()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 
IsRunning() const162 bool Thread::IsRunning() const {
163   return running_;
164 }
165 
SetPriority(ThreadPriority priority)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 
Run(MessageLoop * message_loop)173 void Thread::Run(MessageLoop* message_loop) {
174   message_loop->Run();
175 }
176 
SetThreadWasQuitProperly(bool flag)177 void Thread::SetThreadWasQuitProperly(bool flag) {
178   lazy_tls_bool.Pointer()->Set(flag);
179 }
180 
GetThreadWasQuitProperly()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 
ThreadMain()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