| 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() could return either the thread object or |
| 119 // nullptr. JoinForTesting() cleans up if we get nullptr. |
| 120 if (!detached_thread) |
| 121 detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT); |
| 109 } | 122 } |
| 110 | 123 |
| 111 void Join() { PlatformThread::Join(thread_handle_); } | 124 void Join() { PlatformThread::Join(thread_handle_); } |
| 112 | 125 |
| 113 void WakeUp() { wake_up_event_.Signal(); } | 126 void WakeUp() { wake_up_event_.Signal(); } |
| 114 | 127 |
| 115 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } | 128 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } |
| 116 | 129 |
| 117 private: | 130 private: |
| 118 Thread(SchedulerWorker* outer) | 131 Thread(scoped_refptr<SchedulerWorker> outer) |
| 119 : outer_(outer), | 132 : outer_(std::move(outer)), |
| 120 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, | 133 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, |
| 121 WaitableEvent::InitialState::NOT_SIGNALED), | 134 WaitableEvent::InitialState::NOT_SIGNALED), |
| 122 current_thread_priority_(GetDesiredThreadPriority()) { | 135 current_thread_priority_(GetDesiredThreadPriority()) { |
| 123 DCHECK(outer_); | 136 DCHECK(outer_); |
| 124 } | 137 } |
| 125 | 138 |
| 126 void Initialize() { | 139 void Initialize() { |
| 127 constexpr size_t kDefaultStackSize = 0; | 140 constexpr size_t kDefaultStackSize = 0; |
| 128 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, | 141 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, |
| 129 current_thread_priority_); | 142 current_thread_priority_); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { | 181 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { |
| 169 if (desired_thread_priority == current_thread_priority_) | 182 if (desired_thread_priority == current_thread_priority_) |
| 170 return; | 183 return; |
| 171 | 184 |
| 172 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); | 185 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); |
| 173 current_thread_priority_ = desired_thread_priority; | 186 current_thread_priority_ = desired_thread_priority; |
| 174 } | 187 } |
| 175 | 188 |
| 176 PlatformThreadHandle thread_handle_; | 189 PlatformThreadHandle thread_handle_; |
| 177 | 190 |
| 178 SchedulerWorker* outer_; | 191 scoped_refptr<SchedulerWorker> outer_; |
| 179 | 192 |
| 180 // Event signaled to wake up this thread. | 193 // Event signaled to wake up this thread. |
| 181 WaitableEvent wake_up_event_; | 194 WaitableEvent wake_up_event_; |
| 182 | 195 |
| 183 // Current priority of this thread. May be different from | 196 // Current priority of this thread. May be different from |
| 184 // |outer_->priority_hint_|. | 197 // |outer_->priority_hint_|. |
| 185 ThreadPriority current_thread_priority_; | 198 ThreadPriority current_thread_priority_; |
| 186 | 199 |
| 187 DISALLOW_COPY_AND_ASSIGN(Thread); | 200 DISALLOW_COPY_AND_ASSIGN(Thread); |
| 188 }; | 201 }; |
| 189 | 202 |
| 190 std::unique_ptr<SchedulerWorker> SchedulerWorker::Create( | 203 scoped_refptr<SchedulerWorker> SchedulerWorker::Create( |
| 191 ThreadPriority priority_hint, | 204 ThreadPriority priority_hint, |
| 192 std::unique_ptr<Delegate> delegate, | 205 std::unique_ptr<Delegate> delegate, |
| 193 TaskTracker* task_tracker, | 206 TaskTracker* task_tracker, |
| 194 InitialState initial_state, | 207 InitialState initial_state, |
| 195 SchedulerBackwardCompatibility backward_compatibility) { | 208 SchedulerBackwardCompatibility backward_compatibility) { |
| 196 auto worker = | 209 scoped_refptr<SchedulerWorker> worker( |
| 197 WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate), | 210 new SchedulerWorker(priority_hint, std::move(delegate), task_tracker, |
| 198 task_tracker, backward_compatibility)); | 211 backward_compatibility)); |
| 199 // Creation happens before any other thread can reference this one, so no | 212 // Creation happens before any other thread can reference this one, so no |
| 200 // synchronization is necessary. | 213 // synchronization is necessary. |
| 201 if (initial_state == SchedulerWorker::InitialState::ALIVE) { | 214 if (initial_state == SchedulerWorker::InitialState::ALIVE) { |
| 202 worker->CreateThread(); | 215 worker->CreateThread(); |
| 203 if (!worker->thread_) { | 216 if (!worker->thread_) { |
| 204 return nullptr; | 217 return nullptr; |
| 205 } | 218 } |
| 206 } | 219 } |
| 207 | 220 |
| 208 return worker; | 221 return worker; |
| 209 } | 222 } |
| 210 | 223 |
| 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() { | 224 void SchedulerWorker::WakeUp() { |
| 219 AutoSchedulerLock auto_lock(thread_lock_); | 225 AutoSchedulerLock auto_lock(thread_lock_); |
| 220 | 226 |
| 221 DCHECK(!should_exit_for_testing_.IsSet()); | 227 DCHECK(!join_called_for_testing_.IsSet()); |
| 222 | 228 |
| 223 if (!thread_) | 229 if (!thread_) |
| 224 CreateThreadAssertSynchronized(); | 230 CreateThreadAssertSynchronized(); |
| 225 | 231 |
| 226 if (thread_) | 232 if (thread_) |
| 227 thread_->WakeUp(); | 233 thread_->WakeUp(); |
| 228 } | 234 } |
| 229 | 235 |
| 230 void SchedulerWorker::JoinForTesting() { | 236 void SchedulerWorker::JoinForTesting() { |
| 231 DCHECK(!should_exit_for_testing_.IsSet()); | 237 DCHECK(!join_called_for_testing_.IsSet()); |
| 232 should_exit_for_testing_.Set(); | 238 join_called_for_testing_.Set(); |
| 233 | 239 |
| 234 std::unique_ptr<Thread> thread; | 240 std::unique_ptr<Thread> thread; |
| 235 | 241 |
| 236 { | 242 { |
| 237 AutoSchedulerLock auto_lock(thread_lock_); | 243 AutoSchedulerLock auto_lock(thread_lock_); |
| 238 | 244 |
| 239 if (thread_) { | 245 if (thread_) { |
| 240 // Make sure the thread is awake. It will see that | 246 // Make sure the thread is awake. It will see that |
| 241 // |should_exit_for_testing_| is set and exit shortly after. | 247 // |join_called_for_testing_| is set and exit shortly after. |
| 242 thread_->WakeUp(); | 248 thread_->WakeUp(); |
| 243 thread = std::move(thread_); | 249 thread = std::move(thread_); |
| 244 } | 250 } |
| 245 } | 251 } |
| 246 | 252 |
| 247 if (thread) | 253 if (thread) |
| 248 thread->Join(); | 254 thread->Join(); |
| 249 } | 255 } |
| 250 | 256 |
| 251 bool SchedulerWorker::ThreadAliveForTesting() const { | 257 bool SchedulerWorker::ThreadAliveForTesting() const { |
| 252 AutoSchedulerLock auto_lock(thread_lock_); | 258 AutoSchedulerLock auto_lock(thread_lock_); |
| 253 return !!thread_; | 259 return !!thread_; |
| 254 } | 260 } |
| 255 | 261 |
| 262 void SchedulerWorker::Cleanup() { |
| 263 // |should_exit_| is synchronized with |thread_| for writes here so that we |
| 264 // can maintain access to |thread_| for wakeup. Otherwise, the thread may take |
| 265 // away |thread_| for destruction. |
| 266 AutoSchedulerLock auto_lock(thread_lock_); |
| 267 DCHECK(!should_exit_.IsSet()); |
| 268 if (thread_) { |
| 269 should_exit_.Set(); |
| 270 thread_->WakeUp(); |
| 271 } |
| 272 } |
| 273 |
| 256 SchedulerWorker::SchedulerWorker( | 274 SchedulerWorker::SchedulerWorker( |
| 257 ThreadPriority priority_hint, | 275 ThreadPriority priority_hint, |
| 258 std::unique_ptr<Delegate> delegate, | 276 std::unique_ptr<Delegate> delegate, |
| 259 TaskTracker* task_tracker, | 277 TaskTracker* task_tracker, |
| 260 SchedulerBackwardCompatibility backward_compatibility) | 278 SchedulerBackwardCompatibility backward_compatibility) |
| 261 : priority_hint_(priority_hint), | 279 : priority_hint_(priority_hint), |
| 262 delegate_(std::move(delegate)), | 280 delegate_(std::move(delegate)), |
| 263 task_tracker_(task_tracker) | 281 task_tracker_(task_tracker) |
| 264 #if defined(OS_WIN) | 282 #if defined(OS_WIN) |
| 265 , | 283 , |
| 266 backward_compatibility_(backward_compatibility) | 284 backward_compatibility_(backward_compatibility) |
| 267 #endif | 285 #endif |
| 268 { | 286 { |
| 269 DCHECK(delegate_); | 287 DCHECK(delegate_); |
| 270 DCHECK(task_tracker_); | 288 DCHECK(task_tracker_); |
| 271 } | 289 } |
| 272 | 290 |
| 273 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() { | 291 SchedulerWorker::~SchedulerWorker() { |
| 292 // It is unexpected for |thread_| to be alive and for SchedulerWorker to |
| 293 // destroy since SchedulerWorker owns the delegate needed by |thread_|. |
| 294 // For testing, this generally means JoinForTesting was not called. |
| 295 DCHECK(!thread_); |
| 296 } |
| 297 |
| 298 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject( |
| 299 DetachNotify detach_notify) { |
| 274 AutoSchedulerLock auto_lock(thread_lock_); | 300 AutoSchedulerLock auto_lock(thread_lock_); |
| 275 | 301 |
| 276 // Do not detach if the thread is being joined. | 302 // Do not detach if the thread is being joined. |
| 277 if (!thread_) { | 303 if (!thread_) { |
| 278 DCHECK(should_exit_for_testing_.IsSet()); | 304 DCHECK(join_called_for_testing_.IsSet()); |
| 279 return nullptr; | 305 return nullptr; |
| 280 } | 306 } |
| 281 | 307 |
| 282 // If a wakeup is pending, then a WakeUp() came in while we were deciding to | 308 // 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 | 309 // detach. This means we can't go away anymore since we would break the |
| 284 // guarantee that we call GetWork() after a successful wakeup. | 310 // guarantee that we call GetWork() after a successful wakeup. |
| 285 if (thread_->IsWakeUpPending()) | 311 if (thread_->IsWakeUpPending()) |
| 286 return nullptr; | 312 return nullptr; |
| 287 | 313 |
| 288 // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate | 314 if (detach_notify == DetachNotify::DELEGATE) { |
| 289 // from being used concurrently from an old and a new thread. | 315 // Call OnDetach() within the scope of |thread_lock_| to prevent the |
| 290 delegate_->OnDetach(); | 316 // delegate from being used concurrently from an old and a new thread. |
| 317 delegate_->OnDetach(); |
| 318 } |
| 291 | 319 |
| 292 return std::move(thread_); | 320 return std::move(thread_); |
| 293 } | 321 } |
| 294 | 322 |
| 295 void SchedulerWorker::CreateThread() { | 323 void SchedulerWorker::CreateThread() { |
| 296 thread_ = Thread::Create(this); | 324 thread_ = Thread::Create(make_scoped_refptr(this)); |
| 297 } | 325 } |
| 298 | 326 |
| 299 void SchedulerWorker::CreateThreadAssertSynchronized() { | 327 void SchedulerWorker::CreateThreadAssertSynchronized() { |
| 300 thread_lock_.AssertAcquired(); | 328 thread_lock_.AssertAcquired(); |
| 301 CreateThread(); | 329 CreateThread(); |
| 302 } | 330 } |
| 303 | 331 |
| 332 bool SchedulerWorker::ShouldExit() { |
| 333 return task_tracker_->IsShutdownComplete() || |
| 334 join_called_for_testing_.IsSet() || should_exit_.IsSet(); |
| 335 } |
| 336 |
| 304 } // namespace internal | 337 } // namespace internal |
| 305 } // namespace base | 338 } // namespace base |
| OLD | NEW |