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/sequenced_worker_pool.h" | 5 #include "base/threading/sequenced_worker_pool.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
8 #include <map> | 8 #include <map> |
9 #include <set> | 9 #include <set> |
10 #include <utility> | 10 #include <utility> |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 DISALLOW_COPY_AND_ASSIGN(Worker); | 65 DISALLOW_COPY_AND_ASSIGN(Worker); |
66 }; | 66 }; |
67 | 67 |
68 // Inner ---------------------------------------------------------------------- | 68 // Inner ---------------------------------------------------------------------- |
69 | 69 |
70 class SequencedWorkerPool::Inner { | 70 class SequencedWorkerPool::Inner { |
71 public: | 71 public: |
72 // Take a raw pointer to |worker| to avoid cycles (since we're owned | 72 // Take a raw pointer to |worker| to avoid cycles (since we're owned |
73 // by it). | 73 // by it). |
74 Inner(SequencedWorkerPool* worker_pool, size_t max_threads, | 74 Inner(SequencedWorkerPool* worker_pool, size_t max_threads, |
75 const std::string& thread_name_prefix); | 75 const std::string& thread_name_prefix, |
| 76 TestingObserver* observer); |
76 | 77 |
77 ~Inner(); | 78 ~Inner(); |
78 | 79 |
79 SequenceToken GetSequenceToken(); | 80 SequenceToken GetSequenceToken(); |
80 | 81 |
81 SequenceToken GetNamedSequenceToken(const std::string& name); | 82 SequenceToken GetNamedSequenceToken(const std::string& name); |
82 | 83 |
83 // This function accepts a name and an ID. If the name is null, the | 84 // This function accepts a name and an ID. If the name is null, the |
84 // token ID is used. This allows us to implement the optional name lookup | 85 // token ID is used. This allows us to implement the optional name lookup |
85 // from a single function without having to enter the lock a separate time. | 86 // from a single function without having to enter the lock a separate time. |
86 bool PostTask(const std::string* optional_token_name, | 87 bool PostTask(const std::string* optional_token_name, |
87 SequenceToken sequence_token, | 88 SequenceToken sequence_token, |
88 WorkerShutdown shutdown_behavior, | 89 WorkerShutdown shutdown_behavior, |
89 const tracked_objects::Location& from_here, | 90 const tracked_objects::Location& from_here, |
90 const Closure& task); | 91 const Closure& task); |
91 | 92 |
92 bool RunsTasksOnCurrentThread() const; | 93 bool RunsTasksOnCurrentThread() const; |
93 | 94 |
94 void FlushForTesting(); | 95 void FlushForTesting(); |
95 | 96 |
96 void TriggerSpuriousWorkSignalForTesting(); | 97 void SignalHasWorkForTesting(); |
97 | 98 |
98 int GetWorkSignalCountForTesting() const; | 99 int GetWorkSignalCountForTesting() const; |
99 | 100 |
100 void Shutdown(); | 101 void Shutdown(); |
101 | 102 |
102 void SetTestingObserver(TestingObserver* observer); | |
103 | |
104 // Runs the worker loop on the background thread. | 103 // Runs the worker loop on the background thread. |
105 void ThreadLoop(Worker* this_worker); | 104 void ThreadLoop(Worker* this_worker); |
106 | 105 |
107 private: | 106 private: |
108 // Returns whether there are no more pending tasks and all threads | 107 // Returns whether there are no more pending tasks and all threads |
109 // are idle. Must be called under lock. | 108 // are idle. Must be called under lock. |
110 bool IsIdle() const; | 109 bool IsIdle() const; |
111 | 110 |
112 // Called from within the lock, this converts the given token name into a | 111 // Called from within the lock, this converts the given token name into a |
113 // token ID, creating a new one if necessary. | 112 // token ID, creating a new one if necessary. |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 | 165 |
167 // This lock protects |everything in this class|. Do not read or modify | 166 // This lock protects |everything in this class|. Do not read or modify |
168 // anything without holding this lock. Do not block while holding this | 167 // anything without holding this lock. Do not block while holding this |
169 // lock. | 168 // lock. |
170 mutable Lock lock_; | 169 mutable Lock lock_; |
171 | 170 |
172 // Condition variable that is waited on by worker threads until new | 171 // Condition variable that is waited on by worker threads until new |
173 // tasks are posted or shutdown starts. | 172 // tasks are posted or shutdown starts. |
174 ConditionVariable has_work_cv_; | 173 ConditionVariable has_work_cv_; |
175 | 174 |
176 // Number of times |has_work_| has been signalled. Used for testing. | |
177 int has_work_signal_count_; | |
178 | |
179 // Condition variable that is waited on by non-worker threads (in | 175 // Condition variable that is waited on by non-worker threads (in |
180 // FlushForTesting()) until IsIdle() goes to true. | 176 // FlushForTesting()) until IsIdle() goes to true. |
181 ConditionVariable is_idle_cv_; | 177 ConditionVariable is_idle_cv_; |
182 | 178 |
183 // Condition variable that is waited on by non-worker threads (in | 179 // Condition variable that is waited on by non-worker threads (in |
184 // Shutdown()) until CanShutdown() goes to true. | 180 // Shutdown()) until CanShutdown() goes to true. |
185 ConditionVariable can_shutdown_cv_; | 181 ConditionVariable can_shutdown_cv_; |
186 | 182 |
187 // The maximum number of worker threads we'll create. | 183 // The maximum number of worker threads we'll create. |
188 const size_t max_threads_; | 184 const size_t max_threads_; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 // shutdown. | 217 // shutdown. |
222 size_t blocking_shutdown_pending_task_count_; | 218 size_t blocking_shutdown_pending_task_count_; |
223 | 219 |
224 // Lists all sequence tokens currently executing. | 220 // Lists all sequence tokens currently executing. |
225 std::set<int> current_sequences_; | 221 std::set<int> current_sequences_; |
226 | 222 |
227 // Set when Shutdown is called and no further tasks should be | 223 // Set when Shutdown is called and no further tasks should be |
228 // allowed, though we may still be running existing tasks. | 224 // allowed, though we may still be running existing tasks. |
229 bool shutdown_called_; | 225 bool shutdown_called_; |
230 | 226 |
231 TestingObserver* testing_observer_; | 227 TestingObserver* const testing_observer_; |
232 | 228 |
233 DISALLOW_COPY_AND_ASSIGN(Inner); | 229 DISALLOW_COPY_AND_ASSIGN(Inner); |
234 }; | 230 }; |
235 | 231 |
236 // Worker definitions --------------------------------------------------------- | 232 // Worker definitions --------------------------------------------------------- |
237 | 233 |
238 SequencedWorkerPool::Worker::Worker( | 234 SequencedWorkerPool::Worker::Worker( |
239 const scoped_refptr<SequencedWorkerPool>& worker_pool, | 235 const scoped_refptr<SequencedWorkerPool>& worker_pool, |
240 int thread_number, | 236 int thread_number, |
241 const std::string& prefix) | 237 const std::string& prefix) |
(...skipping 15 matching lines...) Expand all Loading... |
257 worker_pool_->inner_->ThreadLoop(this); | 253 worker_pool_->inner_->ThreadLoop(this); |
258 // Release our cyclic reference once we're done. | 254 // Release our cyclic reference once we're done. |
259 worker_pool_ = NULL; | 255 worker_pool_ = NULL; |
260 } | 256 } |
261 | 257 |
262 // Inner definitions --------------------------------------------------------- | 258 // Inner definitions --------------------------------------------------------- |
263 | 259 |
264 SequencedWorkerPool::Inner::Inner( | 260 SequencedWorkerPool::Inner::Inner( |
265 SequencedWorkerPool* worker_pool, | 261 SequencedWorkerPool* worker_pool, |
266 size_t max_threads, | 262 size_t max_threads, |
267 const std::string& thread_name_prefix) | 263 const std::string& thread_name_prefix, |
| 264 TestingObserver* observer) |
268 : worker_pool_(worker_pool), | 265 : worker_pool_(worker_pool), |
269 last_sequence_number_(0), | 266 last_sequence_number_(0), |
270 lock_(), | 267 lock_(), |
271 has_work_cv_(&lock_), | 268 has_work_cv_(&lock_), |
272 has_work_signal_count_(0), | |
273 is_idle_cv_(&lock_), | 269 is_idle_cv_(&lock_), |
274 can_shutdown_cv_(&lock_), | 270 can_shutdown_cv_(&lock_), |
275 max_threads_(max_threads), | 271 max_threads_(max_threads), |
276 thread_name_prefix_(thread_name_prefix), | 272 thread_name_prefix_(thread_name_prefix), |
277 thread_being_created_(false), | 273 thread_being_created_(false), |
278 waiting_thread_count_(0), | 274 waiting_thread_count_(0), |
279 blocking_shutdown_thread_count_(0), | 275 blocking_shutdown_thread_count_(0), |
280 pending_task_count_(0), | 276 pending_task_count_(0), |
281 blocking_shutdown_pending_task_count_(0), | 277 blocking_shutdown_pending_task_count_(0), |
282 shutdown_called_(false), | 278 shutdown_called_(false), |
283 testing_observer_(NULL) {} | 279 testing_observer_(observer) {} |
284 | 280 |
285 SequencedWorkerPool::Inner::~Inner() { | 281 SequencedWorkerPool::Inner::~Inner() { |
286 // You must call Shutdown() before destroying the pool. | 282 // You must call Shutdown() before destroying the pool. |
287 DCHECK(shutdown_called_); | 283 DCHECK(shutdown_called_); |
288 | 284 |
289 // Need to explicitly join with the threads before they're destroyed or else | 285 // Need to explicitly join with the threads before they're destroyed or else |
290 // they will be running when our object is half torn down. | 286 // they will be running when our object is half torn down. |
291 for (ThreadMap::iterator it = threads_.begin(); it != threads_.end(); ++it) | 287 for (ThreadMap::iterator it = threads_.begin(); it != threads_.end(); ++it) |
292 it->second->Join(); | 288 it->second->Join(); |
293 threads_.clear(); | 289 threads_.clear(); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 AutoLock lock(lock_); | 349 AutoLock lock(lock_); |
354 return ContainsKey(threads_, PlatformThread::CurrentId()); | 350 return ContainsKey(threads_, PlatformThread::CurrentId()); |
355 } | 351 } |
356 | 352 |
357 void SequencedWorkerPool::Inner::FlushForTesting() { | 353 void SequencedWorkerPool::Inner::FlushForTesting() { |
358 AutoLock lock(lock_); | 354 AutoLock lock(lock_); |
359 while (!IsIdle()) | 355 while (!IsIdle()) |
360 is_idle_cv_.Wait(); | 356 is_idle_cv_.Wait(); |
361 } | 357 } |
362 | 358 |
363 void SequencedWorkerPool::Inner::TriggerSpuriousWorkSignalForTesting() { | 359 void SequencedWorkerPool::Inner::SignalHasWorkForTesting() { |
364 SignalHasWork(); | 360 SignalHasWork(); |
365 } | 361 } |
366 | 362 |
367 int SequencedWorkerPool::Inner::GetWorkSignalCountForTesting() const { | |
368 AutoLock lock(lock_); | |
369 return has_work_signal_count_; | |
370 } | |
371 | |
372 void SequencedWorkerPool::Inner::Shutdown() { | 363 void SequencedWorkerPool::Inner::Shutdown() { |
373 // Mark us as terminated and go through and drop all tasks that aren't | 364 // Mark us as terminated and go through and drop all tasks that aren't |
374 // required to run on shutdown. Since no new tasks will get posted once the | 365 // required to run on shutdown. Since no new tasks will get posted once the |
375 // terminated flag is set, this ensures that all remaining tasks are required | 366 // terminated flag is set, this ensures that all remaining tasks are required |
376 // for shutdown whenever the termianted_ flag is set. | 367 // for shutdown whenever the termianted_ flag is set. |
377 { | 368 { |
378 AutoLock lock(lock_); | 369 AutoLock lock(lock_); |
379 | 370 |
380 if (shutdown_called_) | 371 if (shutdown_called_) |
381 return; | 372 return; |
382 shutdown_called_ = true; | 373 shutdown_called_ = true; |
383 | 374 |
384 // Tickle the threads. This will wake up a waiting one so it will know that | 375 // Tickle the threads. This will wake up a waiting one so it will know that |
385 // it can exit, which in turn will wake up any other waiting ones. | 376 // it can exit, which in turn will wake up any other waiting ones. |
386 has_work_cv_.Signal(); | 377 SignalHasWork(); |
387 | 378 |
388 // There are no pending or running tasks blocking shutdown, we're done. | 379 // There are no pending or running tasks blocking shutdown, we're done. |
389 if (CanShutdown()) | 380 if (CanShutdown()) |
390 return; | 381 return; |
391 } | 382 } |
392 | 383 |
393 // If we're here, then something is blocking shutdown. So wait for | 384 // If we're here, then something is blocking shutdown. So wait for |
394 // CanShutdown() to go to true. | 385 // CanShutdown() to go to true. |
395 | 386 |
396 if (testing_observer_) | 387 if (testing_observer_) |
397 testing_observer_->WillWaitForShutdown(); | 388 testing_observer_->WillWaitForShutdown(); |
398 | 389 |
399 TimeTicks shutdown_wait_begin = TimeTicks::Now(); | 390 TimeTicks shutdown_wait_begin = TimeTicks::Now(); |
400 | 391 |
401 { | 392 { |
402 AutoLock lock(lock_); | 393 AutoLock lock(lock_); |
403 while (!CanShutdown()) | 394 while (!CanShutdown()) |
404 can_shutdown_cv_.Wait(); | 395 can_shutdown_cv_.Wait(); |
405 } | 396 } |
406 UMA_HISTOGRAM_TIMES("SequencedWorkerPool.ShutdownDelayTime", | 397 UMA_HISTOGRAM_TIMES("SequencedWorkerPool.ShutdownDelayTime", |
407 TimeTicks::Now() - shutdown_wait_begin); | 398 TimeTicks::Now() - shutdown_wait_begin); |
408 } | 399 } |
409 | 400 |
410 void SequencedWorkerPool::Inner::SetTestingObserver( | |
411 TestingObserver* observer) { | |
412 AutoLock lock(lock_); | |
413 testing_observer_ = observer; | |
414 } | |
415 | |
416 void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { | 401 void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { |
417 { | 402 { |
418 AutoLock lock(lock_); | 403 AutoLock lock(lock_); |
419 DCHECK(thread_being_created_); | 404 DCHECK(thread_being_created_); |
420 thread_being_created_ = false; | 405 thread_being_created_ = false; |
421 std::pair<ThreadMap::iterator, bool> result = | 406 std::pair<ThreadMap::iterator, bool> result = |
422 threads_.insert( | 407 threads_.insert( |
423 std::make_pair(this_worker->tid(), make_linked_ptr(this_worker))); | 408 std::make_pair(this_worker->tid(), make_linked_ptr(this_worker))); |
424 DCHECK(result.second); | 409 DCHECK(result.second); |
425 | 410 |
426 while (true) { | 411 while (true) { |
427 // See GetWork for what delete_these_outside_lock is doing. | 412 // See GetWork for what delete_these_outside_lock is doing. |
428 SequencedTask task; | 413 SequencedTask task; |
429 std::vector<Closure> delete_these_outside_lock; | 414 std::vector<Closure> delete_these_outside_lock; |
430 if (GetWork(&task, &delete_these_outside_lock)) { | 415 if (GetWork(&task, &delete_these_outside_lock)) { |
431 int new_thread_id = WillRunWorkerTask(task); | 416 int new_thread_id = WillRunWorkerTask(task); |
432 { | 417 { |
433 AutoUnlock unlock(lock_); | 418 AutoUnlock unlock(lock_); |
434 // There may be more work available, so wake up another | 419 // There may be more work available, so wake up another |
435 // worker thread. (Technically not required, since we | 420 // worker thread. (Technically not required, since we |
436 // already get a signal for each new task, but it doesn't | 421 // already get a signal for each new task, but it doesn't |
437 // hurt.) | 422 // hurt.) |
438 has_work_cv_.Signal(); | 423 SignalHasWork(); |
439 delete_these_outside_lock.clear(); | 424 delete_these_outside_lock.clear(); |
440 | 425 |
441 // Complete thread creation outside the lock if necessary. | 426 // Complete thread creation outside the lock if necessary. |
442 if (new_thread_id) | 427 if (new_thread_id) |
443 FinishStartingAdditionalThread(new_thread_id); | 428 FinishStartingAdditionalThread(new_thread_id); |
444 | 429 |
445 task.task.Run(); | 430 task.task.Run(); |
446 | 431 |
447 // Make sure our task is erased outside the lock for the same reason | 432 // Make sure our task is erased outside the lock for the same reason |
448 // we do this with delete_these_oustide_lock. | 433 // we do this with delete_these_oustide_lock. |
(...skipping 13 matching lines...) Expand all Loading... |
462 if (IsIdle()) | 447 if (IsIdle()) |
463 is_idle_cv_.Signal(); | 448 is_idle_cv_.Signal(); |
464 has_work_cv_.Wait(); | 449 has_work_cv_.Wait(); |
465 waiting_thread_count_--; | 450 waiting_thread_count_--; |
466 } | 451 } |
467 } | 452 } |
468 } // Release lock_. | 453 } // Release lock_. |
469 | 454 |
470 // We noticed we should exit. Wake up the next worker so it knows it should | 455 // We noticed we should exit. Wake up the next worker so it knows it should |
471 // exit as well (because the Shutdown() code only signals once). | 456 // exit as well (because the Shutdown() code only signals once). |
472 has_work_cv_.Signal(); | 457 SignalHasWork(); |
473 | 458 |
474 // Possibly unblock shutdown. | 459 // Possibly unblock shutdown. |
475 can_shutdown_cv_.Signal(); | 460 can_shutdown_cv_.Signal(); |
476 } | 461 } |
477 | 462 |
478 bool SequencedWorkerPool::Inner::IsIdle() const { | 463 bool SequencedWorkerPool::Inner::IsIdle() const { |
479 lock_.AssertAcquired(); | 464 lock_.AssertAcquired(); |
480 return pending_task_count_ == 0 && waiting_thread_count_ == threads_.size(); | 465 return pending_task_count_ == 0 && waiting_thread_count_ == threads_.size(); |
481 } | 466 } |
482 | 467 |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
679 // Called outside of the lock. | 664 // Called outside of the lock. |
680 DCHECK(thread_number > 0); | 665 DCHECK(thread_number > 0); |
681 | 666 |
682 // The worker is assigned to the list when the thread actually starts, which | 667 // The worker is assigned to the list when the thread actually starts, which |
683 // will manage the memory of the pointer. | 668 // will manage the memory of the pointer. |
684 new Worker(worker_pool_, thread_number, thread_name_prefix_); | 669 new Worker(worker_pool_, thread_number, thread_name_prefix_); |
685 } | 670 } |
686 | 671 |
687 void SequencedWorkerPool::Inner::SignalHasWork() { | 672 void SequencedWorkerPool::Inner::SignalHasWork() { |
688 has_work_cv_.Signal(); | 673 has_work_cv_.Signal(); |
689 { | 674 if (testing_observer_) { |
690 AutoLock lock(lock_); | 675 testing_observer_->OnHasWork(); |
691 ++has_work_signal_count_; | |
692 } | 676 } |
693 } | 677 } |
694 | 678 |
695 bool SequencedWorkerPool::Inner::CanShutdown() const { | 679 bool SequencedWorkerPool::Inner::CanShutdown() const { |
696 lock_.AssertAcquired(); | 680 lock_.AssertAcquired(); |
697 // See PrepareToStartAdditionalThreadIfHelpful for how thread creation works. | 681 // See PrepareToStartAdditionalThreadIfHelpful for how thread creation works. |
698 return !thread_being_created_ && | 682 return !thread_being_created_ && |
699 blocking_shutdown_thread_count_ == 0 && | 683 blocking_shutdown_thread_count_ == 0 && |
700 blocking_shutdown_pending_task_count_ == 0; | 684 blocking_shutdown_pending_task_count_ == 0; |
701 } | 685 } |
702 | 686 |
703 // SequencedWorkerPool -------------------------------------------------------- | 687 // SequencedWorkerPool -------------------------------------------------------- |
704 | 688 |
705 SequencedWorkerPool::SequencedWorkerPool( | 689 SequencedWorkerPool::SequencedWorkerPool( |
706 size_t max_threads, | 690 size_t max_threads, |
707 const std::string& thread_name_prefix) | 691 const std::string& thread_name_prefix) |
708 : constructor_message_loop_(MessageLoopProxy::current()), | 692 : constructor_message_loop_(MessageLoopProxy::current()), |
709 inner_(new Inner(ALLOW_THIS_IN_INITIALIZER_LIST(this), | 693 inner_(new Inner(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
710 max_threads, thread_name_prefix)) { | 694 max_threads, thread_name_prefix, NULL)) { |
711 DCHECK(constructor_message_loop_.get()); | 695 DCHECK(constructor_message_loop_.get()); |
712 } | 696 } |
713 | 697 |
| 698 SequencedWorkerPool::SequencedWorkerPool( |
| 699 size_t max_threads, |
| 700 const std::string& thread_name_prefix, |
| 701 TestingObserver* observer) |
| 702 : constructor_message_loop_(MessageLoopProxy::current()), |
| 703 inner_(new Inner(ALLOW_THIS_IN_INITIALIZER_LIST(this), |
| 704 max_threads, thread_name_prefix, observer)) { |
| 705 DCHECK(constructor_message_loop_.get()); |
| 706 } |
| 707 |
714 SequencedWorkerPool::~SequencedWorkerPool() {} | 708 SequencedWorkerPool::~SequencedWorkerPool() {} |
715 | 709 |
716 void SequencedWorkerPool::OnDestruct() const { | 710 void SequencedWorkerPool::OnDestruct() const { |
717 // TODO(akalin): Once we can easily check if we're on a worker | 711 // TODO(akalin): Once we can easily check if we're on a worker |
718 // thread or not, use that instead of restricting destruction to | 712 // thread or not, use that instead of restricting destruction to |
719 // only the constructor message loop. | 713 // only the constructor message loop. |
720 if (constructor_message_loop_->BelongsToCurrentThread()) | 714 if (constructor_message_loop_->BelongsToCurrentThread()) |
721 delete this; | 715 delete this; |
722 else | 716 else |
723 constructor_message_loop_->DeleteSoon(FROM_HERE, this); | 717 constructor_message_loop_->DeleteSoon(FROM_HERE, this); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
792 } | 786 } |
793 | 787 |
794 bool SequencedWorkerPool::RunsTasksOnCurrentThread() const { | 788 bool SequencedWorkerPool::RunsTasksOnCurrentThread() const { |
795 return inner_->RunsTasksOnCurrentThread(); | 789 return inner_->RunsTasksOnCurrentThread(); |
796 } | 790 } |
797 | 791 |
798 void SequencedWorkerPool::FlushForTesting() { | 792 void SequencedWorkerPool::FlushForTesting() { |
799 inner_->FlushForTesting(); | 793 inner_->FlushForTesting(); |
800 } | 794 } |
801 | 795 |
802 void SequencedWorkerPool::TriggerSpuriousWorkSignalForTesting() { | 796 void SequencedWorkerPool::SignalHasWorkForTesting() { |
803 inner_->TriggerSpuriousWorkSignalForTesting(); | 797 inner_->SignalHasWorkForTesting(); |
804 } | |
805 | |
806 int SequencedWorkerPool::GetWorkSignalCountForTesting() const { | |
807 return inner_->GetWorkSignalCountForTesting(); | |
808 } | 798 } |
809 | 799 |
810 void SequencedWorkerPool::Shutdown() { | 800 void SequencedWorkerPool::Shutdown() { |
811 DCHECK(constructor_message_loop_->BelongsToCurrentThread()); | 801 DCHECK(constructor_message_loop_->BelongsToCurrentThread()); |
812 inner_->Shutdown(); | 802 inner_->Shutdown(); |
813 } | 803 } |
814 | 804 |
815 void SequencedWorkerPool::SetTestingObserver(TestingObserver* observer) { | |
816 inner_->SetTestingObserver(observer); | |
817 } | |
818 | |
819 } // namespace base | 805 } // namespace base |
OLD | NEW |