Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/threading/thread.h" | 5 #include "base/threading/thread.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 26 | 26 |
| 27 // We use this thread-local variable to record whether or not a thread exited | 27 // We use this thread-local variable to record whether or not a thread exited |
| 28 // because its Stop method was called. This allows us to catch cases where | 28 // because its Stop method was called. This allows us to catch cases where |
| 29 // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when | 29 // MessageLoop::QuitWhenIdle() is called directly, which is unexpected when |
| 30 // using a Thread to setup and run a MessageLoop. | 30 // using a Thread to setup and run a MessageLoop. |
| 31 base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool = | 31 base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool = |
| 32 LAZY_INSTANCE_INITIALIZER; | 32 LAZY_INSTANCE_INITIALIZER; |
| 33 | 33 |
| 34 } // namespace | 34 } // namespace |
| 35 | 35 |
| 36 Thread::Options::Options() | 36 Thread::Options::Options() = default; |
| 37 : message_loop_type(MessageLoop::TYPE_DEFAULT), | |
| 38 timer_slack(TIMER_SLACK_NONE), | |
| 39 stack_size(0), | |
| 40 priority(ThreadPriority::NORMAL) { | |
| 41 } | |
| 42 | 37 |
| 43 Thread::Options::Options(MessageLoop::Type type, | 38 Thread::Options::Options(MessageLoop::Type type, size_t size) |
| 44 size_t size) | 39 : message_loop_type(type), stack_size(size) {} |
| 45 : message_loop_type(type), | |
| 46 timer_slack(TIMER_SLACK_NONE), | |
| 47 stack_size(size), | |
| 48 priority(ThreadPriority::NORMAL) { | |
| 49 } | |
| 50 | 40 |
| 51 Thread::Options::Options(const Options& other) = default; | 41 Thread::Options::Options(const Options& other) = default; |
| 52 | 42 |
| 53 Thread::Options::~Options() { | 43 Thread::Options::~Options() = default; |
| 54 } | |
| 55 | 44 |
| 56 Thread::Thread(const std::string& name) | 45 Thread::Thread(const std::string& name) |
| 57 : | 46 : id_event_(WaitableEvent::ResetPolicy::MANUAL, |
| 58 #if defined(OS_WIN) | |
| 59 com_status_(NONE), | |
| 60 #endif | |
| 61 stopping_(false), | |
| 62 running_(false), | |
| 63 thread_(0), | |
| 64 id_(kInvalidThreadId), | |
| 65 id_event_(WaitableEvent::ResetPolicy::MANUAL, | |
| 66 WaitableEvent::InitialState::NOT_SIGNALED), | 47 WaitableEvent::InitialState::NOT_SIGNALED), |
| 67 message_loop_(nullptr), | |
| 68 message_loop_timer_slack_(TIMER_SLACK_NONE), | |
| 69 name_(name), | 48 name_(name), |
| 70 start_event_(WaitableEvent::ResetPolicy::MANUAL, | 49 start_event_(WaitableEvent::ResetPolicy::MANUAL, |
| 71 WaitableEvent::InitialState::NOT_SIGNALED) { | 50 WaitableEvent::InitialState::NOT_SIGNALED) { |
| 51 // Only bind the sequence on Start(): the state is constant between | |
| 52 // construction and Start() and it's thus valid for Start() to be called on | |
| 53 // another sequence as long as every other operation is then performed on that | |
| 54 // sequence. | |
| 55 owning_sequence_checker_.DetachFromSequence(); | |
| 72 } | 56 } |
| 73 | 57 |
| 74 Thread::~Thread() { | 58 Thread::~Thread() { |
| 75 Stop(); | 59 Stop(); |
| 76 } | 60 } |
| 77 | 61 |
| 78 bool Thread::Start() { | 62 bool Thread::Start() { |
| 63 DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); | |
| 64 | |
| 79 Options options; | 65 Options options; |
| 80 #if defined(OS_WIN) | 66 #if defined(OS_WIN) |
| 81 if (com_status_ == STA) | 67 if (com_status_ == STA) |
| 82 options.message_loop_type = MessageLoop::TYPE_UI; | 68 options.message_loop_type = MessageLoop::TYPE_UI; |
| 83 #endif | 69 #endif |
| 84 return StartWithOptions(options); | 70 return StartWithOptions(options); |
| 85 } | 71 } |
| 86 | 72 |
| 87 bool Thread::StartWithOptions(const Options& options) { | 73 bool Thread::StartWithOptions(const Options& options) { |
| 74 DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); | |
| 88 DCHECK(!message_loop_); | 75 DCHECK(!message_loop_); |
| 76 DCHECK(!IsRunning()); | |
| 89 #if defined(OS_WIN) | 77 #if defined(OS_WIN) |
| 90 DCHECK((com_status_ != STA) || | 78 DCHECK((com_status_ != STA) || |
| 91 (options.message_loop_type == MessageLoop::TYPE_UI)); | 79 (options.message_loop_type == MessageLoop::TYPE_UI)); |
| 92 #endif | 80 #endif |
| 93 | 81 |
| 94 // Reset |id_| here to support restarting the thread. | 82 // Reset |id_| here to support restarting the thread. |
| 95 id_event_.Reset(); | 83 id_event_.Reset(); |
| 96 id_ = kInvalidThreadId; | 84 id_ = kInvalidThreadId; |
| 97 | 85 |
| 98 SetThreadWasQuitProperly(false); | 86 SetThreadWasQuitProperly(false); |
| 99 | 87 |
| 100 MessageLoop::Type type = options.message_loop_type; | 88 MessageLoop::Type type = options.message_loop_type; |
| 101 if (!options.message_pump_factory.is_null()) | 89 if (!options.message_pump_factory.is_null()) |
| 102 type = MessageLoop::TYPE_CUSTOM; | 90 type = MessageLoop::TYPE_CUSTOM; |
| 103 | 91 |
| 104 message_loop_timer_slack_ = options.timer_slack; | 92 message_loop_timer_slack_ = options.timer_slack; |
| 105 std::unique_ptr<MessageLoop> message_loop = | 93 std::unique_ptr<MessageLoop> message_loop_owned = |
| 106 MessageLoop::CreateUnbound(type, options.message_pump_factory); | 94 MessageLoop::CreateUnbound(type, options.message_pump_factory); |
| 107 message_loop_ = message_loop.get(); | 95 message_loop_ = message_loop_owned.get(); |
| 108 start_event_.Reset(); | 96 start_event_.Reset(); |
| 109 | 97 |
| 110 // Hold the thread_lock_ while starting a new thread, so that we can make sure | 98 // Hold |thread_lock_| while starting the new thread to synchronize with |
| 111 // that thread_ is populated before the newly created thread accesses it. | 99 // Stop() while it's not guaranteed to be sequenced (until crbug/629139 is |
| 100 // fixed). | |
| 112 { | 101 { |
| 113 AutoLock lock(thread_lock_); | 102 AutoLock lock(thread_lock_); |
| 114 if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, | 103 if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, |
| 115 options.priority)) { | 104 options.priority)) { |
| 116 DLOG(ERROR) << "failed to create thread"; | 105 DLOG(ERROR) << "failed to create thread"; |
| 117 message_loop_ = nullptr; | 106 message_loop_ = nullptr; |
| 118 return false; | 107 return false; |
| 119 } | 108 } |
| 120 } | 109 } |
| 121 | 110 |
| 122 // The ownership of message_loop is managemed by the newly created thread | 111 // The ownership of |message_loop_| is managed by the newly created thread |
| 123 // within the ThreadMain. | 112 // within the ThreadMain. |
| 124 ignore_result(message_loop.release()); | 113 ignore_result(message_loop_owned.release()); |
| 125 | 114 |
| 126 DCHECK(message_loop_); | 115 DCHECK(message_loop_); |
| 127 return true; | 116 return true; |
| 128 } | 117 } |
| 129 | 118 |
| 130 bool Thread::StartAndWaitForTesting() { | 119 bool Thread::StartAndWaitForTesting() { |
| 120 DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); | |
| 131 bool result = Start(); | 121 bool result = Start(); |
| 132 if (!result) | 122 if (!result) |
| 133 return false; | 123 return false; |
| 134 WaitUntilThreadStarted(); | 124 WaitUntilThreadStarted(); |
| 135 return true; | 125 return true; |
| 136 } | 126 } |
| 137 | 127 |
| 138 bool Thread::WaitUntilThreadStarted() const { | 128 bool Thread::WaitUntilThreadStarted() const { |
| 129 DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); | |
| 139 if (!message_loop_) | 130 if (!message_loop_) |
| 140 return false; | 131 return false; |
| 141 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 132 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| 142 start_event_.Wait(); | 133 start_event_.Wait(); |
| 143 return true; | 134 return true; |
| 144 } | 135 } |
| 145 | 136 |
| 146 void Thread::Stop() { | 137 void Thread::Stop() { |
| 138 // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and | |
| 139 // enable this check, until then synchronization with Start() via | |
| 140 // |thread_lock_| is required... | |
|
Wez
2016/07/25 21:20:29
FWIW, this comment as worded implies that callers
gab
2016/07/26 02:48:23
Generally in Chromium, unless specified otherwise,
| |
| 141 // DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); | |
| 147 AutoLock lock(thread_lock_); | 142 AutoLock lock(thread_lock_); |
| 143 | |
| 148 if (thread_.is_null()) | 144 if (thread_.is_null()) |
| 149 return; | 145 return; |
| 150 | 146 |
| 151 StopSoon(); | 147 StopSoon(); |
| 152 | 148 |
| 153 // Wait for the thread to exit. | 149 // Wait for the thread to exit. |
| 154 // | 150 // |
| 155 // TODO(darin): Unfortunately, we need to keep message_loop_ around until | 151 // TODO(darin): Unfortunately, we need to keep |message_loop_| around until |
| 156 // the thread exits. Some consumers are abusing the API. Make them stop. | 152 // the thread exits. Some consumers are abusing the API. Make them stop. |
| 157 // | 153 // |
| 158 PlatformThread::Join(thread_); | 154 PlatformThread::Join(thread_); |
| 159 thread_ = base::PlatformThreadHandle(); | 155 thread_ = base::PlatformThreadHandle(); |
| 160 | 156 |
| 161 // The thread should nullify message_loop_ on exit. | 157 // The thread should nullify |message_loop_| on exit (note: Join() adds an |
| 158 // implicit memory barrier and no lock is thus required for this check). | |
| 162 DCHECK(!message_loop_); | 159 DCHECK(!message_loop_); |
| 163 | 160 |
| 164 stopping_ = false; | 161 stopping_ = false; |
| 165 } | 162 } |
| 166 | 163 |
| 167 void Thread::StopSoon() { | 164 void Thread::StopSoon() { |
| 168 // We should only be called on the same thread that started us. | 165 // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and |
| 169 | 166 // enable this check. |
| 170 DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 167 // DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); |
| 171 | 168 |
| 172 if (stopping_ || !message_loop_) | 169 if (stopping_ || !message_loop_) |
| 173 return; | 170 return; |
| 174 | 171 |
| 175 stopping_ = true; | 172 stopping_ = true; |
| 176 task_runner()->PostTask( | 173 task_runner()->PostTask( |
| 177 FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this))); | 174 FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this))); |
| 178 } | 175 } |
| 179 | 176 |
| 180 PlatformThreadId Thread::GetThreadId() const { | 177 PlatformThreadId Thread::GetThreadId() const { |
| 181 // If the thread is created but not started yet, wait for |id_| being ready. | 178 // If the thread is created but not started yet, wait for |id_| being ready. |
| 182 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 179 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| 183 id_event_.Wait(); | 180 id_event_.Wait(); |
| 184 return id_; | 181 return id_; |
| 185 } | 182 } |
| 186 | 183 |
| 187 bool Thread::IsRunning() const { | 184 bool Thread::IsRunning() const { |
| 188 // If the thread's already started (i.e. message_loop_ is non-null) and | 185 // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and |
| 189 // not yet requested to stop (i.e. stopping_ is false) we can just return | 186 // enable this check. |
| 190 // true. (Note that stopping_ is touched only on the same thread that | 187 // DCHECK(owning_sequence_checker_.CalledOnValidSequencedThread()); |
| 191 // starts / started the new thread so we need no locking here.) | 188 |
| 189 // If the thread's already started (i.e. |message_loop_| is non-null) and not | |
| 190 // yet requested to stop (i.e. |stopping_| is false) we can just return true. | |
| 191 // (Note that |stopping_| is touched only on the same sequence that starts / | |
| 192 // started the new thread so we need no locking here.) | |
| 192 if (message_loop_ && !stopping_) | 193 if (message_loop_ && !stopping_) |
| 193 return true; | 194 return true; |
| 194 // Otherwise check the running_ flag, which is set to true by the new thread | 195 // Otherwise check the |running_| flag, which is set to true by the new thread |
| 195 // only while it is inside Run(). | 196 // only while it is inside Run(). |
| 196 AutoLock lock(running_lock_); | 197 AutoLock lock(running_lock_); |
| 197 return running_; | 198 return running_; |
| 198 } | 199 } |
| 199 | 200 |
| 200 void Thread::Run(RunLoop* run_loop) { | 201 void Thread::Run(RunLoop* run_loop) { |
| 202 // Overridable protected method to be called from our |thread_| only. | |
| 203 DCHECK_EQ(id_, PlatformThread::CurrentId()); | |
|
Wez
2016/07/25 21:22:36
What is this DCHECK intended to protect against? C
gab
2016/07/26 02:48:23
As is often the case with DCHECKs, it's intended a
| |
| 204 | |
| 201 run_loop->Run(); | 205 run_loop->Run(); |
| 202 } | 206 } |
| 203 | 207 |
| 208 // static | |
| 204 void Thread::SetThreadWasQuitProperly(bool flag) { | 209 void Thread::SetThreadWasQuitProperly(bool flag) { |
| 205 lazy_tls_bool.Pointer()->Set(flag); | 210 lazy_tls_bool.Pointer()->Set(flag); |
| 206 } | 211 } |
| 207 | 212 |
| 213 // static | |
| 208 bool Thread::GetThreadWasQuitProperly() { | 214 bool Thread::GetThreadWasQuitProperly() { |
| 209 bool quit_properly = true; | 215 bool quit_properly = true; |
| 210 #ifndef NDEBUG | 216 #ifndef NDEBUG |
| 211 quit_properly = lazy_tls_bool.Pointer()->Get(); | 217 quit_properly = lazy_tls_bool.Pointer()->Get(); |
| 212 #endif | 218 #endif |
| 213 return quit_properly; | 219 return quit_properly; |
| 214 } | 220 } |
| 215 | 221 |
| 216 void Thread::ThreadMain() { | 222 void Thread::ThreadMain() { |
| 217 // First, make GetThreadId() available to avoid deadlocks. It could be called | 223 // First, make GetThreadId() available to avoid deadlocks. It could be called |
| 218 // any place in the following thread initialization code. | 224 // any place in the following thread initialization code. |
| 219 id_ = PlatformThread::CurrentId(); | 225 id_ = PlatformThread::CurrentId(); |
| 220 DCHECK_NE(kInvalidThreadId, id_); | 226 DCHECK_NE(kInvalidThreadId, id_); |
| 221 id_event_.Signal(); | 227 id_event_.Signal(); |
| 222 | 228 |
| 223 // Complete the initialization of our Thread object. | 229 // Complete the initialization of our Thread object. |
| 224 PlatformThread::SetName(name_.c_str()); | 230 PlatformThread::SetName(name_.c_str()); |
| 225 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. | 231 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. |
| 226 | 232 |
| 227 // Lazily initialize the message_loop so that it can run on this thread. | 233 // Lazily initialize the |message_loop| so that it can run on this thread. |
| 228 DCHECK(message_loop_); | 234 DCHECK(message_loop_); |
| 229 std::unique_ptr<MessageLoop> message_loop(message_loop_); | 235 std::unique_ptr<MessageLoop> message_loop(message_loop_); |
| 230 message_loop_->BindToCurrentThread(); | 236 message_loop_->BindToCurrentThread(); |
| 231 message_loop_->SetTimerSlack(message_loop_timer_slack_); | 237 message_loop_->SetTimerSlack(message_loop_timer_slack_); |
| 232 | 238 |
| 233 #if defined(OS_WIN) | 239 #if defined(OS_WIN) |
| 234 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; | 240 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; |
| 235 if (com_status_ != NONE) { | 241 if (com_status_ != NONE) { |
| 236 com_initializer.reset((com_status_ == STA) ? | 242 com_initializer.reset((com_status_ == STA) ? |
| 237 new win::ScopedCOMInitializer() : | 243 new win::ScopedCOMInitializer() : |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 run_loop_ = nullptr; | 284 run_loop_ = nullptr; |
| 279 } | 285 } |
| 280 | 286 |
| 281 void Thread::ThreadQuitHelper() { | 287 void Thread::ThreadQuitHelper() { |
| 282 DCHECK(run_loop_); | 288 DCHECK(run_loop_); |
| 283 run_loop_->QuitWhenIdle(); | 289 run_loop_->QuitWhenIdle(); |
| 284 SetThreadWasQuitProperly(true); | 290 SetThreadWasQuitProperly(true); |
| 285 } | 291 } |
| 286 | 292 |
| 287 } // namespace base | 293 } // namespace base |
| OLD | NEW |