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