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 | |
danakj
2016/07/21 19:43:26
nice comment
gab
2016/07/22 16:02:54
:-)
| |
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 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(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(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 = |
danakj
2016/07/21 19:43:26
can you name this so that there is more difference
gab
2016/07/22 16:02:55
Done.
| |
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.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 if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, |
111 // that thread_ is populated before the newly created thread accesses it. | 99 options.priority)) { |
112 { | 100 DLOG(ERROR) << "failed to create thread"; |
113 AutoLock lock(thread_lock_); | 101 message_loop_ = nullptr; |
danakj
2016/07/21 19:43:26
I think you do need this until http://crbug.com/62
gab
2016/07/22 16:02:55
Hmmm I guess yea :-(, AFAICT there's only one bad
| |
114 if (!PlatformThread::CreateWithPriority(options.stack_size, this, &thread_, | 102 return false; |
115 options.priority)) { | |
116 DLOG(ERROR) << "failed to create thread"; | |
117 message_loop_ = nullptr; | |
118 return false; | |
119 } | |
120 } | 103 } |
121 | 104 |
122 // The ownership of message_loop is managemed by the newly created thread | 105 // The ownership of |message_loop| is managed by the newly created thread |
danakj
2016/07/21 19:43:25
I think saying "|message_loop_|" (ie the member) h
gab
2016/07/22 16:02:55
Done.
| |
123 // within the ThreadMain. | 106 // within the ThreadMain. |
124 ignore_result(message_loop.release()); | 107 ignore_result(message_loop.release()); |
125 | 108 |
126 DCHECK(message_loop_); | 109 DCHECK(message_loop_); |
127 return true; | 110 return true; |
128 } | 111 } |
129 | 112 |
130 bool Thread::StartAndWaitForTesting() { | 113 bool Thread::StartAndWaitForTesting() { |
114 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
131 bool result = Start(); | 115 bool result = Start(); |
132 if (!result) | 116 if (!result) |
133 return false; | 117 return false; |
134 WaitUntilThreadStarted(); | 118 WaitUntilThreadStarted(); |
135 return true; | 119 return true; |
136 } | 120 } |
137 | 121 |
138 bool Thread::WaitUntilThreadStarted() const { | 122 bool Thread::WaitUntilThreadStarted() const { |
123 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
139 if (!message_loop_) | 124 if (!message_loop_) |
140 return false; | 125 return false; |
141 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 126 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
142 start_event_.Wait(); | 127 start_event_.Wait(); |
143 return true; | 128 return true; |
144 } | 129 } |
145 | 130 |
146 void Thread::Stop() { | 131 void Thread::Stop() { |
147 AutoLock lock(thread_lock_); | 132 // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and |
133 // enable this check. | |
134 // DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
135 | |
148 if (thread_.is_null()) | 136 if (thread_.is_null()) |
149 return; | 137 return; |
150 | 138 |
151 StopSoon(); | 139 StopSoon(); |
152 | 140 |
153 // Wait for the thread to exit. | 141 // Wait for the thread to exit. |
154 // | 142 // |
155 // TODO(darin): Unfortunately, we need to keep message_loop_ around until | 143 // TODO(darin): Unfortunately, we need to keep |message_loop_| around until |
156 // the thread exits. Some consumers are abusing the API. Make them stop. | 144 // the thread exits. Some consumers are abusing the API. Make them stop. |
157 // | 145 // |
158 PlatformThread::Join(thread_); | 146 PlatformThread::Join(thread_); |
159 thread_ = base::PlatformThreadHandle(); | 147 thread_ = base::PlatformThreadHandle(); |
160 | 148 |
161 // The thread should nullify message_loop_ on exit. | 149 // The thread should nullify |message_loop_| on exit (note: Join() adds an |
150 // implicit memory barrier and no lock is thus required for this check). | |
162 DCHECK(!message_loop_); | 151 DCHECK(!message_loop_); |
163 | 152 |
164 stopping_ = false; | 153 stopping_ = false; |
165 } | 154 } |
166 | 155 |
167 void Thread::StopSoon() { | 156 void Thread::StopSoon() { |
168 // We should only be called on the same thread that started us. | 157 // TODO(gab): Fix improper usage of this API (http://crbug.com/629139) and |
169 | 158 // enable this check. |
170 DCHECK_NE(GetThreadId(), PlatformThread::CurrentId()); | 159 // DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
171 | 160 |
172 if (stopping_ || !message_loop_) | 161 if (stopping_ || !message_loop_) |
173 return; | 162 return; |
174 | 163 |
175 stopping_ = true; | 164 stopping_ = true; |
176 task_runner()->PostTask( | 165 task_runner()->PostTask( |
177 FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this))); | 166 FROM_HERE, base::Bind(&Thread::ThreadQuitHelper, Unretained(this))); |
178 } | 167 } |
179 | 168 |
180 PlatformThreadId Thread::GetThreadId() const { | 169 PlatformThreadId Thread::GetThreadId() const { |
181 // If the thread is created but not started yet, wait for |id_| being ready. | 170 // If the thread is created but not started yet, wait for |id_| being ready. |
182 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 171 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
183 id_event_.Wait(); | 172 id_event_.Wait(); |
184 return id_; | 173 return id_; |
185 } | 174 } |
186 | 175 |
187 bool Thread::IsRunning() const { | 176 bool Thread::IsRunning() const { |
188 // If the thread's already started (i.e. message_loop_ is non-null) and | 177 // 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 | 178 // enable this check. |
190 // true. (Note that stopping_ is touched only on the same thread that | 179 // DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
191 // starts / started the new thread so we need no locking here.) | 180 |
181 // If the thread's already started (i.e. |message_loop_| is non-null) and not | |
182 // yet requested to stop (i.e. |stopping_| is false) we can just return true. | |
183 // (Note that |stopping_| is touched only on the same sequence that starts / | |
184 // started the new thread so we need no locking here.) | |
192 if (message_loop_ && !stopping_) | 185 if (message_loop_ && !stopping_) |
193 return true; | 186 return true; |
194 // Otherwise check the running_ flag, which is set to true by the new thread | 187 // Otherwise check the |running_| flag, which is set to true by the new thread |
195 // only while it is inside Run(). | 188 // only while it is inside Run(). |
196 AutoLock lock(running_lock_); | 189 AutoLock lock(running_lock_); |
197 return running_; | 190 return running_; |
198 } | 191 } |
199 | 192 |
200 void Thread::Run(RunLoop* run_loop) { | 193 void Thread::Run(RunLoop* run_loop) { |
194 // Overridable protected method to be called from our |thread_| only. | |
195 DCHECK_EQ(id_, PlatformThread::CurrentId()); | |
196 | |
201 run_loop->Run(); | 197 run_loop->Run(); |
202 } | 198 } |
203 | 199 |
200 // static | |
204 void Thread::SetThreadWasQuitProperly(bool flag) { | 201 void Thread::SetThreadWasQuitProperly(bool flag) { |
205 lazy_tls_bool.Pointer()->Set(flag); | 202 lazy_tls_bool.Pointer()->Set(flag); |
206 } | 203 } |
207 | 204 |
205 // static | |
208 bool Thread::GetThreadWasQuitProperly() { | 206 bool Thread::GetThreadWasQuitProperly() { |
209 bool quit_properly = true; | 207 bool quit_properly = true; |
210 #ifndef NDEBUG | 208 #ifndef NDEBUG |
211 quit_properly = lazy_tls_bool.Pointer()->Get(); | 209 quit_properly = lazy_tls_bool.Pointer()->Get(); |
212 #endif | 210 #endif |
213 return quit_properly; | 211 return quit_properly; |
214 } | 212 } |
215 | 213 |
216 void Thread::ThreadMain() { | 214 void Thread::ThreadMain() { |
217 // First, make GetThreadId() available to avoid deadlocks. It could be called | 215 // First, make GetThreadId() available to avoid deadlocks. It could be called |
218 // any place in the following thread initialization code. | 216 // any place in the following thread initialization code. |
219 id_ = PlatformThread::CurrentId(); | 217 id_ = PlatformThread::CurrentId(); |
220 DCHECK_NE(kInvalidThreadId, id_); | 218 DCHECK_NE(kInvalidThreadId, id_); |
221 id_event_.Signal(); | 219 id_event_.Signal(); |
222 | 220 |
223 // Complete the initialization of our Thread object. | 221 // Complete the initialization of our Thread object. |
224 PlatformThread::SetName(name_.c_str()); | 222 PlatformThread::SetName(name_.c_str()); |
225 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. | 223 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. |
226 | 224 |
227 // Lazily initialize the message_loop so that it can run on this thread. | 225 // Lazily initialize the |message_loop| so that it can run on this thread. |
228 DCHECK(message_loop_); | 226 DCHECK(message_loop_); |
229 std::unique_ptr<MessageLoop> message_loop(message_loop_); | 227 std::unique_ptr<MessageLoop> message_loop(message_loop_); |
230 message_loop_->BindToCurrentThread(); | 228 message_loop_->BindToCurrentThread(); |
231 message_loop_->SetTimerSlack(message_loop_timer_slack_); | 229 message_loop_->SetTimerSlack(message_loop_timer_slack_); |
232 | 230 |
233 #if defined(OS_WIN) | 231 #if defined(OS_WIN) |
234 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; | 232 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; |
235 if (com_status_ != NONE) { | 233 if (com_status_ != NONE) { |
236 com_initializer.reset((com_status_ == STA) ? | 234 com_initializer.reset((com_status_ == STA) ? |
237 new win::ScopedCOMInitializer() : | 235 new win::ScopedCOMInitializer() : |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
278 run_loop_ = nullptr; | 276 run_loop_ = nullptr; |
279 } | 277 } |
280 | 278 |
281 void Thread::ThreadQuitHelper() { | 279 void Thread::ThreadQuitHelper() { |
282 DCHECK(run_loop_); | 280 DCHECK(run_loop_); |
283 run_loop_->QuitWhenIdle(); | 281 run_loop_->QuitWhenIdle(); |
284 SetThreadWasQuitProperly(true); | 282 SetThreadWasQuitProperly(true); |
285 } | 283 } |
286 | 284 |
287 } // namespace base | 285 } // namespace base |
OLD | NEW |