OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/task_scheduler/scheduler_worker.h" | 5 #include "base/task_scheduler/scheduler_worker.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
13 #include "base/task_scheduler/task_tracker.h" | 13 #include "base/task_scheduler/task_tracker.h" |
14 | 14 |
15 #if defined(OS_MACOSX) | 15 #if defined(OS_MACOSX) |
16 #include "base/mac/scoped_nsautorelease_pool.h" | 16 #include "base/mac/scoped_nsautorelease_pool.h" |
17 #elif defined(OS_WIN) | 17 #elif defined(OS_WIN) |
18 #include "base/win/scoped_com_initializer.h" | 18 #include "base/win/scoped_com_initializer.h" |
19 #endif | 19 #endif |
20 | 20 |
21 namespace base { | 21 namespace base { |
22 namespace internal { | 22 namespace internal { |
23 | 23 |
24 class SchedulerWorker::Thread : public PlatformThread::Delegate { | 24 class SchedulerWorker::Thread : public PlatformThread::Delegate { |
25 public: | 25 public: |
26 ~Thread() override = default; | 26 ~Thread() override = default; |
27 | 27 |
28 static std::unique_ptr<Thread> Create(SchedulerWorker* outer) { | 28 static std::unique_ptr<Thread> Create(scoped_refptr<SchedulerWorker> outer) { |
29 std::unique_ptr<Thread> thread(new Thread(outer)); | 29 std::unique_ptr<Thread> thread(new Thread(std::move(outer))); |
30 thread->Initialize(); | 30 thread->Initialize(); |
31 if (thread->thread_handle_.is_null()) | 31 if (thread->thread_handle_.is_null()) |
32 return nullptr; | 32 return nullptr; |
33 return thread; | 33 return thread; |
34 } | 34 } |
35 | 35 |
36 // PlatformThread::Delegate. | 36 // PlatformThread::Delegate. |
37 void ThreadMain() override { | 37 void ThreadMain() override { |
38 // Set if this thread was detached. | 38 // Set if this thread was detached. |
39 std::unique_ptr<Thread> detached_thread; | 39 std::unique_ptr<Thread> detached_thread; |
40 | 40 |
41 outer_->delegate_->OnMainEntry(outer_); | 41 outer_->delegate_->OnMainEntry(outer_.get()); |
42 | 42 |
43 // A SchedulerWorker starts out waiting for work. | 43 // A SchedulerWorker starts out waiting for work. |
44 WaitForWork(); | 44 WaitForWork(); |
45 | 45 |
46 #if defined(OS_WIN) | 46 #if defined(OS_WIN) |
47 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; | 47 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; |
48 if (outer_->backward_compatibility_ == | 48 if (outer_->backward_compatibility_ == |
49 SchedulerBackwardCompatibility::INIT_COM_STA) { | 49 SchedulerBackwardCompatibility::INIT_COM_STA) { |
50 com_initializer = MakeUnique<win::ScopedCOMInitializer>(); | 50 com_initializer = MakeUnique<win::ScopedCOMInitializer>(); |
51 } | 51 } |
52 #endif | 52 #endif |
53 | 53 |
54 while (!outer_->task_tracker_->IsShutdownComplete() && | 54 while (!outer_->ShouldExit()) { |
55 !outer_->should_exit_for_testing_.IsSet()) { | |
56 DCHECK(outer_); | 55 DCHECK(outer_); |
57 | 56 |
58 #if defined(OS_MACOSX) | 57 #if defined(OS_MACOSX) |
59 mac::ScopedNSAutoreleasePool autorelease_pool; | 58 mac::ScopedNSAutoreleasePool autorelease_pool; |
60 #endif | 59 #endif |
61 | 60 |
62 UpdateThreadPriority(GetDesiredThreadPriority()); | 61 UpdateThreadPriority(GetDesiredThreadPriority()); |
63 | 62 |
64 // Get the sequence containing the next task to execute. | 63 // Get the sequence containing the next task to execute. |
65 scoped_refptr<Sequence> sequence = outer_->delegate_->GetWork(outer_); | 64 scoped_refptr<Sequence> sequence = |
65 outer_->delegate_->GetWork(outer_.get()); | |
66 if (!sequence) { | 66 if (!sequence) { |
67 if (outer_->delegate_->CanDetach(outer_)) { | 67 if (outer_->delegate_->CanDetach(outer_.get())) { |
68 detached_thread = outer_->Detach(); | 68 detached_thread = outer_->DetachThreadObject(DetachNotify::DELEGATE); |
69 if (detached_thread) { | 69 if (detached_thread) { |
70 outer_ = nullptr; | 70 outer_ = nullptr; |
71 DCHECK_EQ(detached_thread.get(), this); | 71 DCHECK_EQ(detached_thread.get(), this); |
72 PlatformThread::Detach(thread_handle_); | 72 PlatformThread::Detach(thread_handle_); |
73 break; | 73 break; |
74 } | 74 } |
75 } | 75 } |
76 WaitForWork(); | 76 WaitForWork(); |
77 continue; | 77 continue; |
78 } | 78 } |
(...skipping 20 matching lines...) Expand all Loading... | |
99 // sleep if WakeUp() is called while this SchedulerWorker is awake. | 99 // sleep if WakeUp() is called while this SchedulerWorker is awake. |
100 wake_up_event_.Reset(); | 100 wake_up_event_.Reset(); |
101 } | 101 } |
102 | 102 |
103 // If a wake up is pending and we successfully detached, somehow |outer_| | 103 // If a wake up is pending and we successfully detached, somehow |outer_| |
104 // was able to signal us which means it probably thinks we're still alive. | 104 // was able to signal us which means it probably thinks we're still alive. |
105 // This is bad as it will cause the WakeUp to no-op and |outer_| will be | 105 // This is bad as it will cause the WakeUp to no-op and |outer_| will be |
106 // stuck forever. | 106 // stuck forever. |
107 DCHECK(!detached_thread || !IsWakeUpPending()) << | 107 DCHECK(!detached_thread || !IsWakeUpPending()) << |
108 "This thread was detached and woken up at the same time."; | 108 "This thread was detached and woken up at the same time."; |
109 | |
110 // This thread is generally responsible for cleaning itself up except when | |
111 // JoinForTesting() is called. | |
112 // We arrive here in the following cases: | |
113 // Thread Detachment Request: | |
114 // * |detached_thread| will not be nullptr. | |
115 // ShouldExit() returns true: | |
116 // * Shutdown: DetachThreadObject returns the thread object. | |
117 // * Cleanup: DetachThreadObject returns the thread object. | |
118 // * Join: DetachThreadObject returns nullptr. Join cleans up. | |
gab
2017/02/23 19:25:24
It's also possible to receive the thread object in
robliao
2017/02/23 21:04:22
Sure, and done.
| |
119 if (!detached_thread) | |
120 detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT); | |
109 } | 121 } |
110 | 122 |
111 void Join() { PlatformThread::Join(thread_handle_); } | 123 void Join() { PlatformThread::Join(thread_handle_); } |
112 | 124 |
113 void WakeUp() { wake_up_event_.Signal(); } | 125 void WakeUp() { wake_up_event_.Signal(); } |
114 | 126 |
115 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } | 127 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } |
116 | 128 |
117 private: | 129 private: |
118 Thread(SchedulerWorker* outer) | 130 Thread(scoped_refptr<SchedulerWorker> outer) |
119 : outer_(outer), | 131 : outer_(std::move(outer)), |
120 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, | 132 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, |
121 WaitableEvent::InitialState::NOT_SIGNALED), | 133 WaitableEvent::InitialState::NOT_SIGNALED), |
122 current_thread_priority_(GetDesiredThreadPriority()) { | 134 current_thread_priority_(GetDesiredThreadPriority()) { |
123 DCHECK(outer_); | 135 DCHECK(outer_); |
124 } | 136 } |
125 | 137 |
126 void Initialize() { | 138 void Initialize() { |
127 constexpr size_t kDefaultStackSize = 0; | 139 constexpr size_t kDefaultStackSize = 0; |
128 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, | 140 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, |
129 current_thread_priority_); | 141 current_thread_priority_); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
168 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { | 180 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { |
169 if (desired_thread_priority == current_thread_priority_) | 181 if (desired_thread_priority == current_thread_priority_) |
170 return; | 182 return; |
171 | 183 |
172 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); | 184 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); |
173 current_thread_priority_ = desired_thread_priority; | 185 current_thread_priority_ = desired_thread_priority; |
174 } | 186 } |
175 | 187 |
176 PlatformThreadHandle thread_handle_; | 188 PlatformThreadHandle thread_handle_; |
177 | 189 |
178 SchedulerWorker* outer_; | 190 scoped_refptr<SchedulerWorker> outer_; |
179 | 191 |
180 // Event signaled to wake up this thread. | 192 // Event signaled to wake up this thread. |
181 WaitableEvent wake_up_event_; | 193 WaitableEvent wake_up_event_; |
182 | 194 |
183 // Current priority of this thread. May be different from | 195 // Current priority of this thread. May be different from |
184 // |outer_->priority_hint_|. | 196 // |outer_->priority_hint_|. |
185 ThreadPriority current_thread_priority_; | 197 ThreadPriority current_thread_priority_; |
186 | 198 |
187 DISALLOW_COPY_AND_ASSIGN(Thread); | 199 DISALLOW_COPY_AND_ASSIGN(Thread); |
188 }; | 200 }; |
189 | 201 |
190 std::unique_ptr<SchedulerWorker> SchedulerWorker::Create( | 202 scoped_refptr<SchedulerWorker> SchedulerWorker::Create( |
191 ThreadPriority priority_hint, | 203 ThreadPriority priority_hint, |
192 std::unique_ptr<Delegate> delegate, | 204 std::unique_ptr<Delegate> delegate, |
193 TaskTracker* task_tracker, | 205 TaskTracker* task_tracker, |
194 InitialState initial_state, | 206 InitialState initial_state, |
195 SchedulerBackwardCompatibility backward_compatibility) { | 207 SchedulerBackwardCompatibility backward_compatibility) { |
196 auto worker = | 208 scoped_refptr<SchedulerWorker> worker( |
197 WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate), | 209 new SchedulerWorker(priority_hint, std::move(delegate), task_tracker, |
198 task_tracker, backward_compatibility)); | 210 backward_compatibility)); |
199 // Creation happens before any other thread can reference this one, so no | 211 // Creation happens before any other thread can reference this one, so no |
200 // synchronization is necessary. | 212 // synchronization is necessary. |
201 if (initial_state == SchedulerWorker::InitialState::ALIVE) { | 213 if (initial_state == SchedulerWorker::InitialState::ALIVE) { |
202 worker->CreateThread(); | 214 worker->CreateThread(); |
203 if (!worker->thread_) { | 215 if (!worker->thread_) { |
204 return nullptr; | 216 return nullptr; |
205 } | 217 } |
206 } | 218 } |
207 | 219 |
208 return worker; | 220 return worker; |
209 } | 221 } |
210 | 222 |
211 SchedulerWorker::~SchedulerWorker() { | |
212 // It is unexpected for |thread_| to be alive and for SchedulerWorker to | |
213 // destroy since SchedulerWorker owns the delegate needed by |thread_|. | |
214 // For testing, this generally means JoinForTesting was not called. | |
215 DCHECK(!thread_); | |
216 } | |
217 | |
218 void SchedulerWorker::WakeUp() { | 223 void SchedulerWorker::WakeUp() { |
219 AutoSchedulerLock auto_lock(thread_lock_); | 224 AutoSchedulerLock auto_lock(thread_lock_); |
220 | 225 |
221 DCHECK(!should_exit_for_testing_.IsSet()); | 226 DCHECK(!join_called_for_testing_.IsSet()); |
222 | 227 |
223 if (!thread_) | 228 if (!thread_) |
224 CreateThreadAssertSynchronized(); | 229 CreateThreadAssertSynchronized(); |
225 | 230 |
226 if (thread_) | 231 if (thread_) |
227 thread_->WakeUp(); | 232 thread_->WakeUp(); |
228 } | 233 } |
229 | 234 |
230 void SchedulerWorker::JoinForTesting() { | 235 void SchedulerWorker::JoinForTesting() { |
231 DCHECK(!should_exit_for_testing_.IsSet()); | 236 DCHECK(!join_called_for_testing_.IsSet()); |
232 should_exit_for_testing_.Set(); | 237 join_called_for_testing_.Set(); |
233 | 238 |
234 std::unique_ptr<Thread> thread; | 239 std::unique_ptr<Thread> thread; |
235 | 240 |
236 { | 241 { |
237 AutoSchedulerLock auto_lock(thread_lock_); | 242 AutoSchedulerLock auto_lock(thread_lock_); |
238 | 243 |
239 if (thread_) { | 244 if (thread_) { |
240 // Make sure the thread is awake. It will see that | 245 // Make sure the thread is awake. It will see that |
241 // |should_exit_for_testing_| is set and exit shortly after. | 246 // |join_called_for_testing_| is set and exit shortly after. |
242 thread_->WakeUp(); | 247 thread_->WakeUp(); |
243 thread = std::move(thread_); | 248 thread = std::move(thread_); |
244 } | 249 } |
245 } | 250 } |
246 | 251 |
247 if (thread) | 252 if (thread) |
248 thread->Join(); | 253 thread->Join(); |
249 } | 254 } |
250 | 255 |
251 bool SchedulerWorker::ThreadAliveForTesting() const { | 256 bool SchedulerWorker::ThreadAliveForTesting() const { |
252 AutoSchedulerLock auto_lock(thread_lock_); | 257 AutoSchedulerLock auto_lock(thread_lock_); |
253 return !!thread_; | 258 return !!thread_; |
254 } | 259 } |
255 | 260 |
261 void SchedulerWorker::Cleanup() { | |
262 // |should_exit_| is synchronized with |thread_| for writes here so that we | |
263 // can maintain access to |thread_| for wakeup. Otherwise, the thread may take | |
264 // away |thread_| for destruction. | |
265 AutoSchedulerLock auto_lock(thread_lock_); | |
266 DCHECK(!should_exit_.IsSet()); | |
267 if (thread_) { | |
268 should_exit_.Set(); | |
269 thread_->WakeUp(); | |
270 } | |
271 } | |
272 | |
256 SchedulerWorker::SchedulerWorker( | 273 SchedulerWorker::SchedulerWorker( |
257 ThreadPriority priority_hint, | 274 ThreadPriority priority_hint, |
258 std::unique_ptr<Delegate> delegate, | 275 std::unique_ptr<Delegate> delegate, |
259 TaskTracker* task_tracker, | 276 TaskTracker* task_tracker, |
260 SchedulerBackwardCompatibility backward_compatibility) | 277 SchedulerBackwardCompatibility backward_compatibility) |
261 : priority_hint_(priority_hint), | 278 : priority_hint_(priority_hint), |
262 delegate_(std::move(delegate)), | 279 delegate_(std::move(delegate)), |
263 task_tracker_(task_tracker) | 280 task_tracker_(task_tracker) |
264 #if defined(OS_WIN) | 281 #if defined(OS_WIN) |
265 , | 282 , |
266 backward_compatibility_(backward_compatibility) | 283 backward_compatibility_(backward_compatibility) |
267 #endif | 284 #endif |
268 { | 285 { |
269 DCHECK(delegate_); | 286 DCHECK(delegate_); |
270 DCHECK(task_tracker_); | 287 DCHECK(task_tracker_); |
271 } | 288 } |
272 | 289 |
273 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() { | 290 SchedulerWorker::~SchedulerWorker() { |
291 // It is unexpected for |thread_| to be alive and for SchedulerWorker to | |
292 // destroy since SchedulerWorker owns the delegate needed by |thread_|. | |
293 // For testing, this generally means JoinForTesting was not called. | |
294 DCHECK(!thread_); | |
295 } | |
296 | |
297 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject( | |
298 DetachNotify detach_notify) { | |
274 AutoSchedulerLock auto_lock(thread_lock_); | 299 AutoSchedulerLock auto_lock(thread_lock_); |
275 | 300 |
276 // Do not detach if the thread is being joined. | 301 // Do not detach if the thread is being joined. |
277 if (!thread_) { | 302 if (!thread_) { |
278 DCHECK(should_exit_for_testing_.IsSet()); | 303 DCHECK(join_called_for_testing_.IsSet()); |
279 return nullptr; | 304 return nullptr; |
280 } | 305 } |
281 | 306 |
282 // If a wakeup is pending, then a WakeUp() came in while we were deciding to | 307 // If a wakeup is pending, then a WakeUp() came in while we were deciding to |
283 // detach. This means we can't go away anymore since we would break the | 308 // detach. This means we can't go away anymore since we would break the |
284 // guarantee that we call GetWork() after a successful wakeup. | 309 // guarantee that we call GetWork() after a successful wakeup. |
285 if (thread_->IsWakeUpPending()) | 310 if (thread_->IsWakeUpPending()) |
286 return nullptr; | 311 return nullptr; |
287 | 312 |
288 // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate | 313 if (detach_notify == DetachNotify::DELEGATE) { |
289 // from being used concurrently from an old and a new thread. | 314 // Call OnDetach() within the scope of |thread_lock_| to prevent the |
290 delegate_->OnDetach(); | 315 // delegate from being used concurrently from an old and a new thread. |
316 delegate_->OnDetach(); | |
317 } | |
291 | 318 |
292 return std::move(thread_); | 319 return std::move(thread_); |
293 } | 320 } |
294 | 321 |
295 void SchedulerWorker::CreateThread() { | 322 void SchedulerWorker::CreateThread() { |
296 thread_ = Thread::Create(this); | 323 thread_ = Thread::Create(make_scoped_refptr(this)); |
297 } | 324 } |
298 | 325 |
299 void SchedulerWorker::CreateThreadAssertSynchronized() { | 326 void SchedulerWorker::CreateThreadAssertSynchronized() { |
300 thread_lock_.AssertAcquired(); | 327 thread_lock_.AssertAcquired(); |
301 CreateThread(); | 328 CreateThread(); |
302 } | 329 } |
303 | 330 |
331 bool SchedulerWorker::ShouldExit() { | |
332 return task_tracker_->IsShutdownComplete() || | |
333 join_called_for_testing_.IsSet() || should_exit_.IsSet(); | |
334 } | |
335 | |
304 } // namespace internal | 336 } // namespace internal |
305 } // namespace base | 337 } // namespace base |
OLD | NEW |