| Index: base/task_scheduler/task_tracker.cc
|
| diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f1da46925fbb08217e5e5e156e8b6d2c177b51a4
|
| --- /dev/null
|
| +++ b/base/task_scheduler/task_tracker.cc
|
| @@ -0,0 +1,158 @@
|
| +// Copyright 2016 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.
|
| +
|
| +#include "base/task_scheduler/task_tracker.h"
|
| +
|
| +#include "base/callback.h"
|
| +#include "base/debug/task_annotator.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| +
|
| +namespace base {
|
| +namespace internal {
|
| +
|
| +namespace {
|
| +const char kQueueFunctionName[] = "base::PostTask";
|
| +} // namespace
|
| +
|
| +TaskTracker::TaskTracker() = default;
|
| +TaskTracker::~TaskTracker() = default;
|
| +
|
| +void TaskTracker::Shutdown() {
|
| + size_t num_block_shutdown_tasks_posted_during_shutdown_copy;
|
| +
|
| + {
|
| + AutoSchedulerLock auto_lock(lock_);
|
| +
|
| + DCHECK(!shutdown_completed_ && !shutdown_cv_)
|
| + << "TaskTracker::Shutdown() should only be invoked once.";
|
| +
|
| + shutdown_cv_ = lock_.CreateConditionVariable();
|
| +
|
| + // Wait until the number of tasks blocking shutdown is zero.
|
| + while (num_tasks_blocking_shutdown_ != 0)
|
| + shutdown_cv_->Wait();
|
| +
|
| + shutdown_cv_.reset();
|
| + shutdown_completed_ = true;
|
| +
|
| + num_block_shutdown_tasks_posted_during_shutdown_copy =
|
| + num_block_shutdown_tasks_posted_during_shutdown_;
|
| + }
|
| +
|
| + UMA_HISTOGRAM_COUNTS_100(
|
| + "TaskScheduler.BlockShutdownTasksPostedDuringShutdown",
|
| + num_block_shutdown_tasks_posted_during_shutdown_copy);
|
| +}
|
| +
|
| +void TaskTracker::PostTask(
|
| + const Callback<void(scoped_ptr<Task>)>& post_task_callback,
|
| + scoped_ptr<Task> task) {
|
| + DCHECK(!post_task_callback.is_null());
|
| + DCHECK(task);
|
| +
|
| + if (!BeforePostTask(task->traits.shutdown_behavior()))
|
| + return;
|
| +
|
| + debug::TaskAnnotator task_annotator;
|
| + task_annotator.DidQueueTask(kQueueFunctionName, *task);
|
| +
|
| + post_task_callback.Run(std::move(task));
|
| +}
|
| +
|
| +void TaskTracker::RunTask(const Task* task) {
|
| + DCHECK(task);
|
| +
|
| + const TaskShutdownBehavior shutdown_behavior =
|
| + task->traits.shutdown_behavior();
|
| +
|
| + DCHECK(shutdown_behavior != TaskShutdownBehavior::BLOCK_SHUTDOWN ||
|
| + num_tasks_blocking_shutdown_ > 0U);
|
| +
|
| + if (!BeforeRunTask(shutdown_behavior))
|
| + return;
|
| +
|
| + debug::TaskAnnotator task_annotator;
|
| + task_annotator.RunTask(kQueueFunctionName, *task);
|
| +
|
| + AfterRunTask(shutdown_behavior);
|
| +}
|
| +
|
| +bool TaskTracker::IsShuttingDownForTesting() const {
|
| + AutoSchedulerLock auto_lock(lock_);
|
| + return !!shutdown_cv_;
|
| +}
|
| +
|
| +bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
|
| + AutoSchedulerLock auto_lock(lock_);
|
| +
|
| + if (shutdown_completed_) {
|
| + // A BLOCK_SHUTDOWN task posted after shutdown has completed is an ordering
|
| + // bug. This DCHECK aims to catch those early.
|
| + DCHECK_NE(TaskShutdownBehavior::BLOCK_SHUTDOWN, shutdown_behavior);
|
| +
|
| + // No task is allowed to be posted after shutdown.
|
| + return false;
|
| + }
|
| +
|
| + if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
|
| + // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted
|
| + // and the moment they complete their execution.
|
| + ++num_tasks_blocking_shutdown_;
|
| +
|
| + if (shutdown_cv_)
|
| + ++num_block_shutdown_tasks_posted_during_shutdown_;
|
| +
|
| + // A BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
|
| + // completed.
|
| + return true;
|
| + }
|
| +
|
| + // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
|
| + // started.
|
| + return !shutdown_cv_;
|
| +}
|
| +
|
| +bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) {
|
| + AutoSchedulerLock auto_lock(lock_);
|
| +
|
| + if (shutdown_completed_) {
|
| + // A BLOCK_SHUTDOWN task run after shutdown has completed is an ordering
|
| + // bug. This DCHECK aims to catch those early.
|
| + DCHECK_NE(TaskShutdownBehavior::BLOCK_SHUTDOWN, shutdown_behavior);
|
| + return false;
|
| + }
|
| +
|
| + switch (shutdown_behavior) {
|
| + case TaskShutdownBehavior::BLOCK_SHUTDOWN:
|
| + return true;
|
| +
|
| + case TaskShutdownBehavior::SKIP_ON_SHUTDOWN:
|
| + if (shutdown_cv_)
|
| + return false;
|
| +
|
| + // SKIP_ON_SHUTDOWN tasks block shutdown while they are running.
|
| + ++num_tasks_blocking_shutdown_;
|
| + return true;
|
| +
|
| + case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN:
|
| + return !shutdown_cv_;
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) {
|
| + if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN ||
|
| + shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {
|
| + AutoSchedulerLock auto_lock(lock_);
|
| + DCHECK_GT(num_tasks_blocking_shutdown_, 0U);
|
| + --num_tasks_blocking_shutdown_;
|
| + if (num_tasks_blocking_shutdown_ == 0 && shutdown_cv_)
|
| + shutdown_cv_->Signal();
|
| + }
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace base
|
|
|