[email protected] | 06c0c06 | 2012-05-21 18:49:37 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 4 | |
[email protected] | 34b9963 | 2011-01-01 01:01:06 | [diff] [blame] | 5 | #include "base/threading/thread.h" |
[email protected] | eff4aecb | 2008-08-12 18:37:35 | [diff] [blame] | 6 | |
[email protected] | 6e238ba | 2011-11-23 20:45:43 | [diff] [blame] | 7 | #include "base/bind.h" |
[email protected] | f886b7bf | 2008-09-10 10:54:06 | [diff] [blame] | 8 | #include "base/lazy_instance.h" |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 9 | #include "base/location.h" |
eroman | f653ac3a | 2015-03-10 01:06:19 | [diff] [blame] | 10 | #include "base/synchronization/waitable_event.h" |
[email protected] | ee85751 | 2010-05-14 08:24:42 | [diff] [blame] | 11 | #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
[email protected] | 7d342e88 | 2013-01-25 20:41:19 | [diff] [blame] | 12 | #include "base/threading/thread_id_name_manager.h" |
[email protected] | 1357c32 | 2010-12-30 22:18:56 | [diff] [blame] | 13 | #include "base/threading/thread_local.h" |
[email protected] | 3a7b66d | 2012-04-26 16:34:16 | [diff] [blame] | 14 | #include "base/threading/thread_restrictions.h" |
avi | 9ceb8b8 | 2015-12-24 21:53:59 | [diff] [blame] | 15 | #include "build/build_config.h" |
[email protected] | eff4aecb | 2008-08-12 18:37:35 | [diff] [blame] | 16 | |
[email protected] | 458e952 | 2012-10-23 04:33:52 | [diff] [blame] | 17 | #if defined(OS_WIN) |
| 18 | #include "base/win/scoped_com_initializer.h" |
| 19 | #endif |
| 20 | |
[email protected] | 4d9bdfaf | 2008-08-26 05:53:57 | [diff] [blame] | 21 | namespace base { |
| 22 | |
[email protected] | eae9c06 | 2011-01-11 00:50:59 | [diff] [blame] | 23 | namespace { |
| 24 | |
| 25 | // We use this thread-local variable to record whether or not a thread exited |
| 26 | // because its Stop method was called. This allows us to catch cases where |
[email protected] | 91cae259 | 2013-01-10 14:56:17 | [diff] [blame] | 27 | // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when |
| 28 | // using a Thread to setup and run a MessageLoop. |
[email protected] | 6de0fd1d | 2011-11-15 13:31:49 | [diff] [blame] | 29 | base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool = |
| 30 | LAZY_INSTANCE_INITIALIZER; |
[email protected] | eae9c06 | 2011-01-11 00:50:59 | [diff] [blame] | 31 | |
| 32 | } // namespace |
| 33 | |
[email protected] | 6e238ba | 2011-11-23 20:45:43 | [diff] [blame] | 34 | // This is used to trigger the message loop to exit. |
| 35 | void ThreadQuitHelper() { |
[email protected] | 91cae259 | 2013-01-10 14:56:17 | [diff] [blame] | 36 | MessageLoop::current()->QuitWhenIdle(); |
[email protected] | 6e238ba | 2011-11-23 20:45:43 | [diff] [blame] | 37 | Thread::SetThreadWasQuitProperly(true); |
| 38 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 39 | |
[email protected] | 9f0e4f7 | 2013-11-08 06:16:53 | [diff] [blame] | 40 | Thread::Options::Options() |
| 41 | : message_loop_type(MessageLoop::TYPE_DEFAULT), |
[email protected] | 8a3e4e1 | 2014-06-24 19:51:02 | [diff] [blame] | 42 | timer_slack(TIMER_SLACK_NONE), |
toyoshim | 66f71ec | 2015-06-09 06:37:06 | [diff] [blame] | 43 | stack_size(0), |
| 44 | priority(ThreadPriority::NORMAL) { |
[email protected] | 9f0e4f7 | 2013-11-08 06:16:53 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | Thread::Options::Options(MessageLoop::Type type, |
| 48 | size_t size) |
| 49 | : message_loop_type(type), |
[email protected] | 8a3e4e1 | 2014-06-24 19:51:02 | [diff] [blame] | 50 | timer_slack(TIMER_SLACK_NONE), |
toyoshim | 66f71ec | 2015-06-09 06:37:06 | [diff] [blame] | 51 | stack_size(size), |
| 52 | priority(ThreadPriority::NORMAL) { |
[email protected] | 9f0e4f7 | 2013-11-08 06:16:53 | [diff] [blame] | 53 | } |
| 54 | |
vmpstr | e65942b | 2016-02-25 00:50:31 | [diff] [blame] | 55 | Thread::Options::Options(const Options& other) = default; |
| 56 | |
[email protected] | 9f0e4f7 | 2013-11-08 06:16:53 | [diff] [blame] | 57 | Thread::Options::~Options() { |
| 58 | } |
| 59 | |
[email protected] | f6c8a6a7 | 2014-04-18 23:32:40 | [diff] [blame] | 60 | Thread::Thread(const std::string& name) |
[email protected] | 458e952 | 2012-10-23 04:33:52 | [diff] [blame] | 61 | : |
| 62 | #if defined(OS_WIN) |
| 63 | com_status_(NONE), |
| 64 | #endif |
[email protected] | a0297bd5 | 2010-07-23 05:32:38 | [diff] [blame] | 65 | stopping_(false), |
[email protected] | 06c0c06 | 2012-05-21 18:49:37 | [diff] [blame] | 66 | running_(false), |
[email protected] | 6548726 | 2008-08-26 06:06:01 | [diff] [blame] | 67 | thread_(0), |
toyoshim | c41261e | 2015-07-24 09:25:16 | [diff] [blame] | 68 | id_(kInvalidThreadId), |
gab | 75d7233 | 2016-06-01 21:15:33 | [diff] [blame] | 69 | id_event_(WaitableEvent::ResetPolicy::MANUAL, |
| 70 | WaitableEvent::InitialState::NOT_SIGNALED), |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 71 | message_loop_(nullptr), |
| 72 | message_loop_timer_slack_(TIMER_SLACK_NONE), |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 73 | name_(name), |
gab | 75d7233 | 2016-06-01 21:15:33 | [diff] [blame] | 74 | start_event_(WaitableEvent::ResetPolicy::MANUAL, |
| 75 | WaitableEvent::InitialState::NOT_SIGNALED) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | Thread::~Thread() { |
| 79 | Stop(); |
| 80 | } |
| 81 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 82 | bool Thread::Start() { |
[email protected] | 458e952 | 2012-10-23 04:33:52 | [diff] [blame] | 83 | Options options; |
| 84 | #if defined(OS_WIN) |
| 85 | if (com_status_ == STA) |
| 86 | options.message_loop_type = MessageLoop::TYPE_UI; |
| 87 | #endif |
| 88 | return StartWithOptions(options); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 89 | } |
| 90 | |
[email protected] | 4d9bdfaf | 2008-08-26 05:53:57 | [diff] [blame] | 91 | bool Thread::StartWithOptions(const Options& options) { |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 92 | DCHECK(!message_loop_); |
[email protected] | 458e952 | 2012-10-23 04:33:52 | [diff] [blame] | 93 | #if defined(OS_WIN) |
| 94 | DCHECK((com_status_ != STA) || |
| 95 | (options.message_loop_type == MessageLoop::TYPE_UI)); |
| 96 | #endif |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 97 | |
toyoshim | c41261e | 2015-07-24 09:25:16 | [diff] [blame] | 98 | // Reset |id_| here to support restarting the thread. |
| 99 | id_event_.Reset(); |
| 100 | id_ = kInvalidThreadId; |
| 101 | |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 102 | SetThreadWasQuitProperly(false); |
| 103 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 104 | MessageLoop::Type type = options.message_loop_type; |
| 105 | if (!options.message_pump_factory.is_null()) |
| 106 | type = MessageLoop::TYPE_CUSTOM; |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 107 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 108 | message_loop_timer_slack_ = options.timer_slack; |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 109 | std::unique_ptr<MessageLoop> message_loop = |
| 110 | MessageLoop::CreateUnbound(type, options.message_pump_factory); |
kinuko | 547a48b | 2015-06-30 01:31:54 | [diff] [blame] | 111 | message_loop_ = message_loop.get(); |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 112 | start_event_.Reset(); |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 113 | |
| 114 | // Hold the thread_lock_ while starting a new thread, so that we can make sure |
| 115 | // that thread_ is populated before the newly created thread accesses it. |
| 116 | { |
| 117 | AutoLock lock(thread_lock_); |
toyoshim | 9f3199f | 2015-07-18 09:49:33 | [diff] [blame] | 118 | if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, |
| 119 | options.priority)) { |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 120 | DLOG(ERROR) << "failed to create thread"; |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 121 | message_loop_ = nullptr; |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 122 | return false; |
| 123 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 124 | } |
| 125 | |
kinuko | 547a48b | 2015-06-30 01:31:54 | [diff] [blame] | 126 | // The ownership of message_loop is managemed by the newly created thread |
| 127 | // within the ThreadMain. |
| 128 | ignore_result(message_loop.release()); |
| 129 | |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 130 | DCHECK(message_loop_); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 131 | return true; |
| 132 | } |
| 133 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 134 | bool Thread::StartAndWaitForTesting() { |
| 135 | bool result = Start(); |
| 136 | if (!result) |
| 137 | return false; |
| 138 | WaitUntilThreadStarted(); |
| 139 | return true; |
| 140 | } |
| 141 | |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 142 | bool Thread::WaitUntilThreadStarted() const { |
| 143 | if (!message_loop_) |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 144 | return false; |
| 145 | base::ThreadRestrictions::ScopedAllowWait allow_wait; |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 146 | start_event_.Wait(); |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 147 | return true; |
| 148 | } |
| 149 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 150 | void Thread::Stop() { |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 151 | AutoLock lock(thread_lock_); |
| 152 | if (thread_.is_null()) |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 153 | return; |
| 154 | |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 155 | StopSoon(); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 156 | |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 157 | // Wait for the thread to exit. |
[email protected] | 4d9bdfaf | 2008-08-26 05:53:57 | [diff] [blame] | 158 | // |
| 159 | // TODO(darin): Unfortunately, we need to keep message_loop_ around until |
| 160 | // the thread exits. Some consumers are abusing the API. Make them stop. |
| 161 | // |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 162 | PlatformThread::Join(thread_); |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 163 | thread_ = base::PlatformThreadHandle(); |
[email protected] | eff4aecb | 2008-08-12 18:37:35 | [diff] [blame] | 164 | |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 165 | // The thread should nullify message_loop_ on exit. |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 166 | DCHECK(!message_loop_); |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 167 | |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 168 | stopping_ = false; |
[email protected] | eff4aecb | 2008-08-12 18:37:35 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | void Thread::StopSoon() { |
[email protected] | 4d9bdfaf | 2008-08-26 05:53:57 | [diff] [blame] | 172 | // We should only be called on the same thread that started us. |
[email protected] | 1b3b5068 | 2010-05-19 08:44:48 | [diff] [blame] | 173 | |
toyoshim | c41261e | 2015-07-24 09:25:16 | [diff] [blame] | 174 | DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); |
[email protected] | eff4aecb | 2008-08-12 18:37:35 | [diff] [blame] | 175 | |
[email protected] | 3c92e24 | 2009-09-21 17:03:46 | [diff] [blame] | 176 | if (stopping_ || !message_loop_) |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 177 | return; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 178 | |
[email protected] | 88f33312 | 2009-09-14 23:47:38 | [diff] [blame] | 179 | stopping_ = true; |
skyostil | 054861d | 2015-04-30 19:06:15 | [diff] [blame] | 180 | task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 181 | } |
| 182 | |
toyoshim | c41261e | 2015-07-24 09:25:16 | [diff] [blame] | 183 | PlatformThreadId Thread::GetThreadId() const { |
| 184 | // If the thread is created but not started yet, wait for |id_| being ready. |
| 185 | base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| 186 | id_event_.Wait(); |
| 187 | return id_; |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 188 | } |
| 189 | |
[email protected] | 06c0c06 | 2012-05-21 18:49:37 | [diff] [blame] | 190 | bool Thread::IsRunning() const { |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 191 | // If the thread's already started (i.e. message_loop_ is non-null) and |
| 192 | // not yet requested to stop (i.e. stopping_ is false) we can just return |
| 193 | // true. (Note that stopping_ is touched only on the same thread that |
| 194 | // starts / started the new thread so we need no locking here.) |
| 195 | if (message_loop_ && !stopping_) |
| 196 | return true; |
| 197 | // Otherwise check the running_ flag, which is set to true by the new thread |
| 198 | // only while it is inside Run(). |
| 199 | AutoLock lock(running_lock_); |
[email protected] | 06c0c06 | 2012-05-21 18:49:37 | [diff] [blame] | 200 | return running_; |
| 201 | } |
| 202 | |
[email protected] | 828bd79 | 2009-09-10 05:00:20 | [diff] [blame] | 203 | void Thread::Run(MessageLoop* message_loop) { |
| 204 | message_loop->Run(); |
| 205 | } |
| 206 | |
[email protected] | eae9c06 | 2011-01-11 00:50:59 | [diff] [blame] | 207 | void Thread::SetThreadWasQuitProperly(bool flag) { |
| 208 | lazy_tls_bool.Pointer()->Set(flag); |
| 209 | } |
| 210 | |
| 211 | bool Thread::GetThreadWasQuitProperly() { |
| 212 | bool quit_properly = true; |
| 213 | #ifndef NDEBUG |
| 214 | quit_properly = lazy_tls_bool.Pointer()->Get(); |
| 215 | #endif |
| 216 | return quit_properly; |
| 217 | } |
| 218 | |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 219 | void Thread::ThreadMain() { |
toyoshim | c41261e | 2015-07-24 09:25:16 | [diff] [blame] | 220 | // First, make GetThreadId() available to avoid deadlocks. It could be called |
| 221 | // any place in the following thread initialization code. |
| 222 | id_ = PlatformThread::CurrentId(); |
| 223 | DCHECK_NE(kInvalidThreadId, id_); |
| 224 | id_event_.Signal(); |
| 225 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 226 | // Complete the initialization of our Thread object. |
| 227 | PlatformThread::SetName(name_.c_str()); |
| 228 | ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 229 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 230 | // Lazily initialize the message_loop so that it can run on this thread. |
| 231 | DCHECK(message_loop_); |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 232 | std::unique_ptr<MessageLoop> message_loop(message_loop_); |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 233 | message_loop_->BindToCurrentThread(); |
| 234 | message_loop_->set_thread_name(name_); |
| 235 | message_loop_->SetTimerSlack(message_loop_timer_slack_); |
[email protected] | e9ba26d | 2008-08-21 09:46:32 | [diff] [blame] | 236 | |
[email protected] | 458e952 | 2012-10-23 04:33:52 | [diff] [blame] | 237 | #if defined(OS_WIN) |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 238 | std::unique_ptr<win::ScopedCOMInitializer> com_initializer; |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 239 | if (com_status_ != NONE) { |
| 240 | com_initializer.reset((com_status_ == STA) ? |
| 241 | new win::ScopedCOMInitializer() : |
| 242 | new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); |
glider | 787e334 | 2015-05-18 13:24:19 | [diff] [blame] | 243 | } |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 244 | #endif |
| 245 | |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 246 | // Let the thread do extra initialization. |
| 247 | Init(); |
| 248 | |
| 249 | { |
| 250 | AutoLock lock(running_lock_); |
| 251 | running_ = true; |
| 252 | } |
| 253 | |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 254 | start_event_.Signal(); |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 255 | |
| 256 | Run(message_loop_); |
| 257 | |
| 258 | { |
| 259 | AutoLock lock(running_lock_); |
| 260 | running_ = false; |
| 261 | } |
| 262 | |
| 263 | // Let the thread do extra cleanup. |
| 264 | CleanUp(); |
| 265 | |
| 266 | #if defined(OS_WIN) |
| 267 | com_initializer.reset(); |
| 268 | #endif |
| 269 | |
John Abd-El-Malek | a6342a9 | 2015-11-24 06:29:56 | [diff] [blame] | 270 | if (message_loop->type() != MessageLoop::TYPE_CUSTOM) { |
| 271 | // Assert that MessageLoop::QuitWhenIdle was called by ThreadQuitHelper. |
| 272 | // Don't check for custom message pumps, because their shutdown might not |
| 273 | // allow this. |
| 274 | DCHECK(GetThreadWasQuitProperly()); |
| 275 | } |
kinuko | 7f68f87 | 2015-05-23 11:38:37 | [diff] [blame] | 276 | |
| 277 | // We can't receive messages anymore. |
| 278 | // (The message loop is destructed at the end of this block) |
toyoshim | 53818d55 | 2015-08-06 09:03:08 | [diff] [blame] | 279 | message_loop_ = nullptr; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 280 | } |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 281 | |
[email protected] | 4d9bdfaf | 2008-08-26 05:53:57 | [diff] [blame] | 282 | } // namespace base |