summaryrefslogtreecommitdiffstats
path: root/base/threading
diff options
context:
space:
mode:
authornhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-02 08:31:57 +0000
committernhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-02 08:31:57 +0000
commit113d2ac3da59a7a7635b0e0b287a938f11559c5b (patch)
treeab7656f3473aaee0381ef27b4df01089f95bec5e /base/threading
parent3b9c4bdeeab22b88fc8fd5a71c2728939c7e17d9 (diff)
downloadchromium_src-113d2ac3da59a7a7635b0e0b287a938f11559c5b.zip
chromium_src-113d2ac3da59a7a7635b0e0b287a938f11559c5b.tar.gz
chromium_src-113d2ac3da59a7a7635b0e0b287a938f11559c5b.tar.bz2
- Implement delayed task posting for SequencedWorkerPool.
- Copy unit tests from MessageLoop. BUG=119657 TEST=base_unittests Review URL: https://chromiumcodereview.appspot.com/10828299 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@159657 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/threading')
-rw-r--r--base/threading/sequenced_worker_pool.cc234
-rw-r--r--base/threading/sequenced_worker_pool.h42
-rw-r--r--base/threading/sequenced_worker_pool_unittest.cc19
3 files changed, 199 insertions, 96 deletions
diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc
index b9a38c6..2258ace 100644
--- a/base/threading/sequenced_worker_pool.cc
+++ b/base/threading/sequenced_worker_pool.cc
@@ -41,21 +41,42 @@ struct SequencedTask : public TrackingInfo {
SequencedTask()
: sequence_token_id(0),
trace_id(0),
+ sequence_task_number(0),
shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}
explicit SequencedTask(const tracked_objects::Location& from_here)
: base::TrackingInfo(from_here, TimeTicks()),
sequence_token_id(0),
trace_id(0),
+ sequence_task_number(0),
shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}
~SequencedTask() {}
int sequence_token_id;
int trace_id;
+ int64 sequence_task_number;
SequencedWorkerPool::WorkerShutdown shutdown_behavior;
tracked_objects::Location posted_from;
Closure task;
+
+ // Non-delayed tasks and delayed tasks are managed together by time-to-run
+ // order. We calculate the time by adding the posted time and the given delay.
+ TimeTicks time_to_run;
+};
+
+struct SequencedTaskLessThan {
+ public:
+ bool operator()(const SequencedTask& lhs, const SequencedTask& rhs) const {
+ if (lhs.time_to_run < rhs.time_to_run)
+ return true;
+
+ if (lhs.time_to_run > rhs.time_to_run)
+ return false;
+
+ // If the time happen to match, then we use the sequence number to decide.
+ return lhs.sequence_task_number < rhs.sequence_task_number;
+ }
};
// SequencedWorkerPoolTaskRunner ---------------------------------------------
@@ -78,13 +99,6 @@ class SequencedWorkerPoolTaskRunner : public TaskRunner {
private:
virtual ~SequencedWorkerPoolTaskRunner();
- // Helper function for posting a delayed task. Asserts that the delay is
- // zero because non-zero delays are not yet supported.
- bool PostDelayedTaskAssertZeroDelay(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay);
-
const scoped_refptr<SequencedWorkerPool> pool_;
const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;
@@ -106,25 +120,17 @@ bool SequencedWorkerPoolTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- return PostDelayedTaskAssertZeroDelay(from_here, task, delay);
+ if (delay == TimeDelta()) {
+ return pool_->PostWorkerTaskWithShutdownBehavior(
+ from_here, task, shutdown_behavior_);
+ }
+ return pool_->PostDelayedWorkerTask(from_here, task, delay);
}
bool SequencedWorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
return pool_->RunsTasksOnCurrentThread();
}
-bool SequencedWorkerPoolTaskRunner::PostDelayedTaskAssertZeroDelay(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
- // TODO(francoisk777@gmail.com): Change the following two statements once
- // SequencedWorkerPool supports non-zero delays.
- DCHECK_EQ(delay.InMillisecondsRoundedUp(), 0)
- << "SequencedWorkerPoolTaskRunner does not yet support non-zero delays";
- return pool_->PostWorkerTaskWithShutdownBehavior(
- from_here, task, shutdown_behavior_);
-}
-
// SequencedWorkerPoolSequencedTaskRunner ------------------------------------
// A SequencedTaskRunner which posts tasks to a SequencedWorkerPool with a
// fixed sequence token.
@@ -152,13 +158,6 @@ class SequencedWorkerPoolSequencedTaskRunner : public SequencedTaskRunner {
private:
virtual ~SequencedWorkerPoolSequencedTaskRunner();
- // Helper function for posting a delayed task. Asserts that the delay is
- // zero because non-zero delays are not yet supported.
- bool PostDelayedTaskAssertZeroDelay(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay);
-
const scoped_refptr<SequencedWorkerPool> pool_;
const SequencedWorkerPool::SequenceToken token_;
@@ -185,7 +184,11 @@ bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- return PostDelayedTaskAssertZeroDelay(from_here, task, delay);
+ if (delay == TimeDelta()) {
+ return pool_->PostSequencedWorkerTaskWithShutdownBehavior(
+ token_, from_here, task, shutdown_behavior_);
+ }
+ return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
}
bool SequencedWorkerPoolSequencedTaskRunner::RunsTasksOnCurrentThread() const {
@@ -196,20 +199,7 @@ bool SequencedWorkerPoolSequencedTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- return PostDelayedTaskAssertZeroDelay(from_here, task, delay);
-}
-
-bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTaskAssertZeroDelay(
- const tracked_objects::Location& from_here,
- const Closure& task,
- TimeDelta delay) {
- // TODO(francoisk777@gmail.com): Change the following two statements once
- // SequencedWorkerPool supports non-zero delays.
- DCHECK_EQ(delay.InMillisecondsRoundedUp(), 0)
- << "SequencedWorkerPoolSequencedTaskRunner does not yet support non-zero"
- " delays";
- return pool_->PostSequencedWorkerTaskWithShutdownBehavior(
- token_, from_here, task, shutdown_behavior_);
+ return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
}
// Create a process-wide unique ID to represent this task in trace events. This
@@ -275,7 +265,8 @@ class SequencedWorkerPool::Inner {
SequenceToken sequence_token,
WorkerShutdown shutdown_behavior,
const tracked_objects::Location& from_here,
- const Closure& task);
+ const Closure& task,
+ TimeDelta delay);
bool RunsTasksOnCurrentThread() const;
@@ -293,6 +284,12 @@ class SequencedWorkerPool::Inner {
void ThreadLoop(Worker* this_worker);
private:
+ enum GetWorkStatus {
+ GET_WORK_FOUND,
+ GET_WORK_NOT_FOUND,
+ GET_WORK_WAIT,
+ };
+
// Returns whether there are no more pending tasks and all threads
// are idle. Must be called under lock.
bool IsIdle() const;
@@ -301,11 +298,27 @@ class SequencedWorkerPool::Inner {
// 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);
+ // Called from within the lock, this returns the next sequence task number.
+ int64 LockedGetNextSequenceTaskNumber();
+
+ // Gets new task. There are 3 cases depending on the return value:
+ //
+ // 1) If the return value is |GET_WORK_FOUND|, |task| is filled in and should
+ // be run immediately.
+ // 2) If the return value is |GET_WORK_NOT_FOUND|, there are no tasks to run,
+ // and |task| is not filled in. In this case, the caller should wait until
+ // a task is posted.
+ // 3) If the return value is |GET_WORK_WAIT|, there are no tasks to run
+ // immediately, and |task| is not filled in. Likewise, |wait_time| is
+ // filled in the time to wait until the next task to run. In this case, the
+ // caller should wait the time.
+ //
+ // In any case, the calling code should clear the given
+ // delete_these_outside_lock vector the next time the lock is released.
+ // See the implementation for a more detailed description.
+ GetWorkStatus GetWork(SequencedTask* task,
+ TimeDelta* wait_time,
+ std::vector<Closure>* delete_these_outside_lock);
// Peforms init and cleanup around running the given task. WillRun...
// returns the value from PrepareToStartAdditionalThreadIfNecessary.
@@ -394,13 +407,16 @@ class SequencedWorkerPool::Inner {
// or SKIP_ON_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_;
+ // A set of all pending tasks in time-to-run order. These are tasks that are
+ // either waiting for a thread to run on, waiting for their time to run,
+ // or blocked on a previous task in their sequence. We have to iterate over
+ // the tasks by time-to-run order, so we use the set instead of the
+ // traditional priority_queue.
+ typedef std::set<SequencedTask, SequencedTaskLessThan> PendingTaskSet;
+ PendingTaskSet pending_tasks_;
+
+ // The next sequence number for a new sequenced task.
+ int64 next_sequence_task_number_;
// Number of tasks in the pending_tasks_ list that are marked as blocking
// shutdown.
@@ -465,7 +481,7 @@ SequencedWorkerPool::Inner::Inner(
thread_being_created_(false),
waiting_thread_count_(0),
blocking_shutdown_thread_count_(0),
- pending_task_count_(0),
+ next_sequence_task_number_(0),
blocking_shutdown_pending_task_count_(0),
trace_id_(0),
shutdown_called_(false),
@@ -503,7 +519,9 @@ bool SequencedWorkerPool::Inner::PostTask(
SequenceToken sequence_token,
WorkerShutdown shutdown_behavior,
const tracked_objects::Location& from_here,
- const Closure& task) {
+ const Closure& task,
+ TimeDelta delay) {
+ DCHECK(delay == TimeDelta() || shutdown_behavior == SKIP_ON_SHUTDOWN);
SequencedTask sequenced(from_here);
sequenced.sequence_token_id = sequence_token.id_;
sequenced.shutdown_behavior = shutdown_behavior;
@@ -511,6 +529,7 @@ bool SequencedWorkerPool::Inner::PostTask(
sequenced.task =
shutdown_behavior == BLOCK_SHUTDOWN ?
base::MakeCriticalClosure(task) : task;
+ sequenced.time_to_run = TimeTicks::Now() + delay;
int create_thread_id = 0;
{
@@ -524,12 +543,13 @@ bool SequencedWorkerPool::Inner::PostTask(
TRACE_EVENT_FLOW_BEGIN0("task", "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))));
+ sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber();
+
// Now that we have the lock, apply the named token rules.
if (optional_token_name)
sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);
- pending_tasks_.push_back(sequenced);
- pending_task_count_++;
+ pending_tasks_.insert(sequenced);
if (shutdown_behavior == BLOCK_SHUTDOWN)
blocking_shutdown_pending_task_count_++;
@@ -626,8 +646,11 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// See GetWork for what delete_these_outside_lock is doing.
SequencedTask task;
+ TimeDelta wait_time;
std::vector<Closure> delete_these_outside_lock;
- if (GetWork(&task, &delete_these_outside_lock)) {
+ GetWorkStatus status =
+ GetWork(&task, &wait_time, &delete_these_outside_lock);
+ if (status == GET_WORK_FOUND) {
TRACE_EVENT_FLOW_END0("task", "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))));
TRACE_EVENT2("task", "SequencedWorkerPool::ThreadLoop",
@@ -671,13 +694,24 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
// shutdown_called_ is set. There may be some tasks stuck
// behind running ones with the same sequence token, but
// additional threads won't help this case.
- if (shutdown_called_)
+ if (shutdown_called_ &&
+ blocking_shutdown_pending_task_count_ == 0)
break;
waiting_thread_count_++;
// This is the only time that IsIdle() can go to true.
if (IsIdle())
is_idle_cv_.Signal();
- has_work_cv_.Wait();
+
+ switch (status) {
+ case GET_WORK_NOT_FOUND:
+ has_work_cv_.Wait();
+ break;
+ case GET_WORK_WAIT:
+ has_work_cv_.TimedWait(wait_time);
+ break;
+ default:
+ NOTREACHED();
+ }
waiting_thread_count_--;
}
}
@@ -693,7 +727,7 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
bool SequencedWorkerPool::Inner::IsIdle() const {
lock_.AssertAcquired();
- return pending_task_count_ == 0 && waiting_thread_count_ == threads_.size();
+ return pending_tasks_.empty() && waiting_thread_count_ == threads_.size();
}
int SequencedWorkerPool::Inner::LockedGetNamedTokenID(
@@ -712,14 +746,20 @@ int SequencedWorkerPool::Inner::LockedGetNamedTokenID(
return result.id_;
}
-bool SequencedWorkerPool::Inner::GetWork(
+int64 SequencedWorkerPool::Inner::LockedGetNextSequenceTaskNumber() {
+ lock_.AssertAcquired();
+ // We assume that we never create enough tasks to wrap around.
+ return next_sequence_task_number_++;
+}
+
+SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
SequencedTask* task,
+ TimeDelta* wait_time,
std::vector<Closure>* delete_these_outside_lock) {
lock_.AssertAcquired();
- DCHECK_EQ(pending_tasks_.size(), pending_task_count_);
UMA_HISTOGRAM_COUNTS_100("SequencedWorkerPool.TaskCount",
- static_cast<int>(pending_task_count_));
+ static_cast<int>(pending_tasks_.size()));
// Find the next task with a sequence token that's not currently in use.
// If the token is in use, that means another thread is running something
@@ -743,9 +783,13 @@ bool SequencedWorkerPool::Inner::GetWork(
// we would pop the head element off of that tasks pending list and add it
// to the priority queue. Then we would run the first item in the priority
// queue.
- bool found_task = false;
+
+ GetWorkStatus status = GET_WORK_NOT_FOUND;
int unrunnable_tasks = 0;
- std::list<SequencedTask>::iterator i = pending_tasks_.begin();
+ PendingTaskSet::iterator i = pending_tasks_.begin();
+ // We assume that the loop below doesn't take too long and so we can just do
+ // a single call to TimeTicks::Now().
+ const TimeTicks current_time = TimeTicks::Now();
while (i != pending_tasks_.end()) {
if (!IsSequenceTokenRunnable(i->sequence_token_id)) {
unrunnable_tasks++;
@@ -753,6 +797,13 @@ bool SequencedWorkerPool::Inner::GetWork(
continue;
}
+ if (i->time_to_run > current_time) {
+ // The time to run has not come yet.
+ *wait_time = i->time_to_run - current_time;
+ status = GET_WORK_WAIT;
+ break;
+ }
+
if (shutdown_called_ && i->shutdown_behavior != BLOCK_SHUTDOWN) {
// We're shutting down and the task we just found isn't blocking
// shutdown. Delete it and get more work.
@@ -770,18 +821,16 @@ bool SequencedWorkerPool::Inner::GetWork(
// vector they passed to us once the lock is exited to make this
// happen.
delete_these_outside_lock->push_back(i->task);
- i = pending_tasks_.erase(i);
- pending_task_count_--;
+ pending_tasks_.erase(i++);
} else {
// Found a runnable task.
*task = *i;
- i = pending_tasks_.erase(i);
- pending_task_count_--;
+ pending_tasks_.erase(i);
if (task->shutdown_behavior == BLOCK_SHUTDOWN) {
blocking_shutdown_pending_task_count_--;
}
- found_task = true;
+ status = GET_WORK_FOUND;
break;
}
}
@@ -791,7 +840,7 @@ bool SequencedWorkerPool::Inner::GetWork(
// frequently "some", we should consider the optimization above.
UMA_HISTOGRAM_COUNTS_100("SequencedWorkerPool.UnrunnableTaskCount",
unrunnable_tasks);
- return found_task;
+ return status;
}
int SequencedWorkerPool::Inner::WillRunWorkerTask(const SequencedTask& task) {
@@ -881,7 +930,7 @@ int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() {
threads_.size() < max_threads_ &&
waiting_thread_count_ == 0) {
// We could use an additional thread if there's work to be done.
- for (std::list<SequencedTask>::iterator i = pending_tasks_.begin();
+ for (PendingTaskSet::const_iterator i = pending_tasks_.begin();
i != pending_tasks_.end(); ++i) {
if (IsSequenceTokenRunnable(i->sequence_token_id)) {
// Found a runnable task, mark the thread as being started.
@@ -981,7 +1030,17 @@ bool SequencedWorkerPool::PostWorkerTask(
const tracked_objects::Location& from_here,
const Closure& task) {
return inner_->PostTask(NULL, SequenceToken(), BLOCK_SHUTDOWN,
- from_here, task);
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostDelayedWorkerTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ WorkerShutdown shutdown_behavior =
+ delay == TimeDelta() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
+ return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
+ from_here, task, delay);
}
bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
@@ -989,7 +1048,7 @@ bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
const Closure& task,
WorkerShutdown shutdown_behavior) {
return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
- from_here, task);
+ from_here, task, TimeDelta());
}
bool SequencedWorkerPool::PostSequencedWorkerTask(
@@ -997,7 +1056,18 @@ bool SequencedWorkerPool::PostSequencedWorkerTask(
const tracked_objects::Location& from_here,
const Closure& task) {
return inner_->PostTask(NULL, sequence_token, BLOCK_SHUTDOWN,
- from_here, task);
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostDelayedSequencedWorkerTask(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ WorkerShutdown shutdown_behavior =
+ delay == TimeDelta() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
+ return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
+ from_here, task, delay);
}
bool SequencedWorkerPool::PostNamedSequencedWorkerTask(
@@ -1006,7 +1076,7 @@ bool SequencedWorkerPool::PostNamedSequencedWorkerTask(
const Closure& task) {
DCHECK(!token_name.empty());
return inner_->PostTask(&token_name, SequenceToken(), BLOCK_SHUTDOWN,
- from_here, task);
+ from_here, task, TimeDelta());
}
bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior(
@@ -1015,16 +1085,14 @@ bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior(
const Closure& task,
WorkerShutdown shutdown_behavior) {
return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
- from_here, task);
+ from_here, task, TimeDelta());
}
bool SequencedWorkerPool::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) {
- // TODO(akalin): Add support for non-zero delays.
- DCHECK_EQ(delay.InMillisecondsRoundedUp(), 0);
- return PostWorkerTask(from_here, task);
+ return PostDelayedWorkerTask(from_here, task, delay);
}
bool SequencedWorkerPool::RunsTasksOnCurrentThread() const {
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h
index 2befd19..72abd2b 100644
--- a/base/threading/sequenced_worker_pool.h
+++ b/base/threading/sequenced_worker_pool.h
@@ -160,18 +160,24 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
SequenceToken GetNamedSequenceToken(const std::string& name);
// Returns a SequencedTaskRunner wrapper which posts to this
- // SequencedWorkerPool using the given sequence token.
+ // SequencedWorkerPool using the given sequence token. Tasks with nonzero
+ // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
+ // are posted with BLOCK_SHUTDOWN behavior.
scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunner(
SequenceToken token);
// Returns a SequencedTaskRunner wrapper which posts to this
- // SequencedWorkerPool using the given sequence token and shutdown behavior.
+ // SequencedWorkerPool using the given sequence token. Tasks with nonzero
+ // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
+ // are posted with the given shutdown behavior.
scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunnerWithShutdownBehavior(
SequenceToken token,
WorkerShutdown shutdown_behavior);
// Returns a TaskRunner wrapper which posts to this SequencedWorkerPool using
- // the given shutdown behavior.
+ // the given shutdown behavior. Tasks with nonzero delay are posted with
+ // SKIP_ON_SHUTDOWN behavior and tasks with zero delay are posted with the
+ // given shutdown behavior.
scoped_refptr<TaskRunner> GetTaskRunnerWithShutdownBehavior(
WorkerShutdown shutdown_behavior);
@@ -198,6 +204,19 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
bool PostWorkerTask(const tracked_objects::Location& from_here,
const Closure& task);
+ // Same as PostWorkerTask but allows a delay to be specified (although doing
+ // so changes the shutdown behavior). The task will be run after the given
+ // delay has elapsed.
+ //
+ // If the delay is nonzero, the task won't be guaranteed to run to completion
+ // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
+ // If the delay is zero, this behaves exactly like PostWorkerTask, i.e. the
+ // task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ bool PostDelayedWorkerTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
// Same as PostWorkerTask but allows specification of the shutdown behavior.
bool PostWorkerTaskWithShutdownBehavior(
const tracked_objects::Location& from_here,
@@ -225,6 +244,21 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
const tracked_objects::Location& from_here,
const Closure& task);
+ // Same as PostSequencedWorkerTask but allows a delay to be specified
+ // (although doing so changes the shutdown behavior). The task will be run
+ // after the given delay has elapsed.
+ //
+ // If the delay is nonzero, the task won't be guaranteed to run to completion
+ // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
+ // If the delay is zero, this behaves exactly like PostSequencedWorkerTask,
+ // i.e. the task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ bool PostDelayedSequencedWorkerTask(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
// Same as PostSequencedWorkerTask but allows specification of the shutdown
// behavior.
bool PostSequencedWorkerTaskWithShutdownBehavior(
@@ -233,7 +267,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
const Closure& task,
WorkerShutdown shutdown_behavior);
- // TaskRunner implementation. Forwards to PostWorkerTask().
+ // TaskRunner implementation. Forwards to PostDelayedWorkerTask().
virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) OVERRIDE;
diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc
index 66fafb7..2dcda60 100644
--- a/base/threading/sequenced_worker_pool_unittest.cc
+++ b/base/threading/sequenced_worker_pool_unittest.cc
@@ -640,15 +640,16 @@ class SequencedWorkerPoolTaskRunnerTestDelegate {
}
void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
+ pool_owner_->pool()->FlushForTesting();
pool_owner_->pool()->Shutdown();
// Don't reset |pool_owner_| here, as the test may still hold a
// reference to the pool.
}
bool TaskRunnerHandlesNonZeroDelays() const {
- // TODO(akalin): Set this to true once SequencedWorkerPool handles
- // non-zero delays.
- return false;
+ return true;
}
private:
@@ -679,6 +680,8 @@ class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate {
}
void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
pool_owner_->pool()->FlushForTesting();
pool_owner_->pool()->Shutdown();
// Don't reset |pool_owner_| here, as the test may still hold a
@@ -686,9 +689,7 @@ class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate {
}
bool TaskRunnerHandlesNonZeroDelays() const {
- // TODO(akalin): Set this to true once SequencedWorkerPool handles
- // non-zero delays.
- return false;
+ return true;
}
private:
@@ -720,6 +721,8 @@ class SequencedWorkerPoolSequencedTaskRunnerTestDelegate {
}
void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
pool_owner_->pool()->FlushForTesting();
pool_owner_->pool()->Shutdown();
// Don't reset |pool_owner_| here, as the test may still hold a
@@ -727,9 +730,7 @@ class SequencedWorkerPoolSequencedTaskRunnerTestDelegate {
}
bool TaskRunnerHandlesNonZeroDelays() const {
- // TODO(akalin): Set this to true once SequencedWorkerPool handles
- // non-zero delays.
- return false;
+ return true;
}
private: