Index: base/threading/sequenced_worker_pool.h |
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h |
index f5770a72b966e21bf56cfadc15de3edb0ea36877..2bd3d581f5fb52eeac54c9d8ca229df9f115c603 100644 |
--- a/base/threading/sequenced_worker_pool.h |
+++ b/base/threading/sequenced_worker_pool.h |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
@@ -6,13 +6,22 @@ |
#define BASE_THREADING_SEQUENCED_WORKER_POOL_H_ |
#pragma once |
+#include <cstddef> |
+#include <list> |
+#include <map> |
+#include <set> |
#include <string> |
+#include <vector> |
+#include "base/atomicops.h" |
+#include "base/base_export.h" |
+#include "base/basictypes.h" |
#include "base/callback.h" |
#include "base/memory/linked_ptr.h" |
#include "base/memory/ref_counted.h" |
+#include "base/synchronization/condition_variable.h" |
brettw
2012/02/24 05:43:34
One of the reasons I liked the inner class was tha
|
+#include "base/synchronization/lock.h" |
#include "base/tracked_objects.h" |
-#include "base/base_export.h" |
namespace base { |
@@ -46,7 +55,8 @@ namespace base { |
// not enforce shutdown semantics or allow us to specify how many worker |
// threads to run. For the typical use case of random background work, we don't |
// necessarily want to be super aggressive about creating threads. |
-class BASE_EXPORT SequencedWorkerPool { |
+class BASE_EXPORT SequencedWorkerPool |
+ : public RefCountedThreadSafe<SequencedWorkerPool> { |
public: |
// Defines what should happen to a task posted to the worker pool on shutdown. |
enum WorkerShutdown { |
@@ -87,10 +97,10 @@ class BASE_EXPORT SequencedWorkerPool { |
}; |
// Opaque identifier that defines sequencing of tasks posted to the worker |
- // pool. See NewSequenceToken(). |
+ // pool. |
class SequenceToken { |
public: |
- explicit SequenceToken() : id_(0) {} |
+ SequenceToken() : id_(0) {} |
~SequenceToken() {} |
bool Equals(const SequenceToken& other) const { |
@@ -100,7 +110,7 @@ class BASE_EXPORT SequencedWorkerPool { |
private: |
friend class SequencedWorkerPool; |
- SequenceToken(int id) : id_(id) {} |
+ explicit SequenceToken(int id) : id_(id) {} |
int id_; |
}; |
@@ -116,7 +126,6 @@ class BASE_EXPORT SequencedWorkerPool { |
// and a prefix for the thread name to ad in debugging. |
SequencedWorkerPool(size_t max_threads, |
const std::string& thread_name_prefix); |
- ~SequencedWorkerPool(); |
// Returns a unique token that can be used to sequence tasks posted to |
// PostSequencedWorkerTask(). Valid tokens are alwys nonzero. |
@@ -149,12 +158,12 @@ class BASE_EXPORT SequencedWorkerPool { |
// Returns true if the task was posted successfully. This may fail during |
// shutdown regardless of the specified ShutdownBehavior. |
bool PostWorkerTask(const tracked_objects::Location& from_here, |
- const base::Closure& task); |
+ const Closure& task); |
// Same as PostWorkerTask but allows specification of the shutdown behavior. |
bool PostWorkerTaskWithShutdownBehavior( |
const tracked_objects::Location& from_here, |
- const base::Closure& task, |
+ const Closure& task, |
WorkerShutdown shutdown_behavior); |
// Like PostWorkerTask above, but provides sequencing semantics. This means |
@@ -170,20 +179,20 @@ class BASE_EXPORT SequencedWorkerPool { |
// shutdown regardless of the specified ShutdownBehavior. |
bool PostSequencedWorkerTask(SequenceToken sequence_token, |
const tracked_objects::Location& from_here, |
- const base::Closure& task); |
+ const Closure& task); |
// Like PostSequencedWorkerTask above, but allows you to specify a named |
// token, which saves an extra call to GetNamedSequenceToken. |
bool PostNamedSequencedWorkerTask(const std::string& token_name, |
const tracked_objects::Location& from_here, |
- const base::Closure& task); |
+ const Closure& task); |
// Same as PostSequencedWorkerTask but allows specification of the shutdown |
// behavior. |
bool PostSequencedWorkerTaskWithShutdownBehavior( |
SequenceToken sequence_token, |
const tracked_objects::Location& from_here, |
- const base::Closure& task, |
+ const Closure& task, |
WorkerShutdown shutdown_behavior); |
// Blocks until all pending tasks are complete. This should only be called in |
@@ -206,13 +215,135 @@ class BASE_EXPORT SequencedWorkerPool { |
void SetTestingObserver(TestingObserver* observer); |
private: |
- class Inner; |
+ friend class RefCountedThreadSafe<SequencedWorkerPool>; |
class Worker; |
- friend class Inner; |
- friend class Worker; |
+ struct SequencedTask { |
+ SequencedTask(); |
+ ~SequencedTask(); |
+ |
+ int sequence_token_id; |
+ WorkerShutdown shutdown_behavior; |
+ tracked_objects::Location location; |
+ Closure task; |
+ }; |
+ |
+ ~SequencedWorkerPool(); |
+ |
+ // This function accepts a name and an ID. If the name is null, the |
+ // token ID is used. This allows us to implement the optional name lookup |
+ // from a single function without having to enter the lock a separate time. |
+ bool PostTaskHelper(const std::string* optional_token_name, |
+ SequenceToken sequence_token, |
+ WorkerShutdown shutdown_behavior, |
+ const tracked_objects::Location& from_here, |
+ const Closure& task); |
+ |
+ // Runs the worker loop on the background thread. |
+ void ThreadLoop(Worker* this_worker); |
+ |
+ // Called from within the lock, this converts the given token name into a |
+ // token ID, creating a new one if necessary. |
+ int LockedGetNamedTokenID(const std::string& name); |
+ |
+ // The calling code should clear the given delete_these_oustide_lock |
+ // vector the next time the lock is released. See the implementation for |
+ // a more detailed description. |
+ bool GetWork(SequencedTask* task, |
+ std::vector<Closure>* delete_these_outside_lock); |
+ |
+ // Peforms init and cleanup around running the given task. WillRun... |
+ // returns the value from PrepareToStartAdditionalThreadIfNecessary. |
+ // The calling code should call FinishStartingAdditionalThread once the |
+ // lock is released if the return values is nonzero. |
+ int WillRunWorkerTask(const SequencedTask& task); |
+ void DidRunWorkerTask(const SequencedTask& task); |
+ |
+ // Returns true if there are no threads currently running the given |
+ // sequence token. |
+ bool IsSequenceTokenRunnable(int sequence_token_id) const; |
+ |
+ // Checks if all threads are busy and the addition of one more could run an |
+ // additional task waiting in the queue. This must be called from within |
+ // the lock. |
+ // |
+ // If another thread is helpful, this will mark the thread as being in the |
+ // process of starting and returns the index of the new thread which will be |
+ // 0 or more. The caller should then call FinishStartingAdditionalThread to |
+ // complete initialization once the lock is released. |
+ // |
+ // If another thread is not necessary, returne 0; |
+ // |
+ // See the implementedion for more. |
+ int PrepareToStartAdditionalThreadIfHelpful(); |
+ |
+ // The second part of thread creation after |
+ // PrepareToStartAdditionalThreadIfHelpful with the thread number it |
+ // generated. This actually creates the thread and should be called outside |
+ // the lock to avoid blocking important work starting a thread in the lock. |
+ void FinishStartingAdditionalThread(int thread_number); |
+ |
+ // Checks whether there is work left that's blocking shutdown. Must be |
+ // called inside the lock. |
+ bool CanShutdown() const; |
+ |
+ // The last sequence number used. Managed by GetSequenceToken, since this |
+ // only does threadsafe increment operations, you do not need to hold the |
+ // lock. |
+ volatile subtle::Atomic32 last_sequence_number_; |
+ |
+ // This lock protects |everything in this class|. Do not read or modify |
+ // anything without holding this lock. Do not block while holding this |
+ // lock. |
+ Lock lock_; |
+ |
+ // Condition variable used to wake up worker threads when a task is runnable. |
+ ConditionVariable cond_var_; |
+ |
+ // The maximum number of worker threads we'll create. |
+ size_t max_threads_; |
+ |
+ std::string thread_name_prefix_; |
+ |
+ // Associates all known sequence token names with their IDs. |
+ std::map<std::string, int> named_sequence_tokens_; |
+ |
+ // Owning pointers to all threads we've created so far. Since we lazily |
+ // create threads, this may be less than max_threads_ and will be initially |
+ // empty. |
+ std::vector<linked_ptr<Worker> > threads_; |
+ |
+ // Set to true when we're in the process of creating another thread. |
+ // See PrepareToStartAdditionalThreadIfHelpful for more. |
+ bool thread_being_created_; |
+ |
+ // Number of threads currently waiting for work. |
+ size_t waiting_thread_count_; |
+ |
+ // Number of threads currently running tasks that have the BLOCK_SHUTDOWN |
+ // flag set. |
+ size_t blocking_shutdown_thread_count_; |
+ |
+ // In-order list of all pending tasks. These are tasks waiting for a thread |
+ // to run on or that are blocked on a previous task in their sequence. |
+ // |
+ // We maintain the pending_task_count_ separately for metrics because |
+ // list.size() can be linear time. |
+ std::list<SequencedTask> pending_tasks_; |
+ size_t pending_task_count_; |
+ |
+ // Number of tasks in the pending_tasks_ list that are marked as blocking |
+ // shutdown. |
+ size_t blocking_shutdown_pending_task_count_; |
+ |
+ // Lists all sequence tokens currently executing. |
+ std::set<int> current_sequences_; |
+ |
+ // Set when Shutdown is called and no further tasks should be |
+ // allowed, though we may still be running existing tasks. |
+ bool shutdown_called_; |
- scoped_refptr<Inner> inner_; |
+ TestingObserver* testing_observer_; |
DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPool); |
}; |