123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- // Copyright 2014 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/cancelable_task_tracker.h"
- #include <stddef.h>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/feature_list.h"
- #include "base/location.h"
- #include "base/memory/ref_counted.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/task/scoped_set_task_priority_for_current_thread.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/task_runner.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- namespace base {
- namespace {
- void RunOrPostToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
- OnceClosure closure) {
- if (task_runner->RunsTasksInCurrentSequence())
- std::move(closure).Run();
- else
- task_runner->PostTask(FROM_HERE, std::move(closure));
- }
- // TODO(https://crbug.com/1009795): Remove these once we have established
- // whether off-sequence cancelation is worthwhile.
- // These values are persisted to logs. Entries should not be renumbered and
- // numeric values should never be reused.
- enum class TaskStatus {
- kSameSequenceLive = 0,
- kOffSequenceLive = 1,
- kSameSequenceCanceled = 2,
- kOffSequenceCanceled = 3,
- kMaxValue = kOffSequenceCanceled,
- };
- void UmaRecordTaskDuration(bool same_sequence,
- bool background,
- bool canceled,
- TimeDelta duration) {
- #define DECLARE_HISTOGRAM(suffix) \
- Histogram::FactoryTimeGet( \
- "Scheduler.CancelableTaskTracker.TaskDuration2_" suffix, \
- Milliseconds(1), Seconds(10), 50, Histogram::kUmaTargetedHistogramFlag)
- static HistogramBase* histograms[] = {
- DECLARE_HISTOGRAM("LiveForegroundOffSequence"),
- DECLARE_HISTOGRAM("LiveForegroundSameSequence"),
- DECLARE_HISTOGRAM("LiveBackgroundOffSequence"),
- DECLARE_HISTOGRAM("LiveBackgroundSameSequence"),
- DECLARE_HISTOGRAM("CanceledForegroundOffSequence"),
- DECLARE_HISTOGRAM("CanceledForegroundSameSequence"),
- DECLARE_HISTOGRAM("CanceledBackgroundOffSequence"),
- DECLARE_HISTOGRAM("CanceledBackgroundSameSequence")};
- int i = (same_sequence ? 1 : 0) + (background ? 2 : 0) + (canceled ? 4 : 0);
- histograms[i]->AddTimeMillisecondsGranularity(duration);
- }
- const base::Feature kAllowOffSequenceTaskCancelation{
- "AllowOffSequenceTaskCancelation", base::FEATURE_ENABLED_BY_DEFAULT};
- bool AllowOffSequenceTaskCancelation() {
- if (!base::FeatureList::GetInstance())
- return true;
- return base::FeatureList::IsEnabled(kAllowOffSequenceTaskCancelation);
- }
- } // namespace
- // static
- const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
- CancelableTaskTracker::CancelableTaskTracker() {
- weak_this_ = weak_factory_.GetWeakPtr();
- }
- CancelableTaskTracker::~CancelableTaskTracker() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- TryCancelAll();
- }
- CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
- TaskRunner* task_runner,
- const Location& from_here,
- OnceClosure task) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- CHECK(weak_this_);
- return PostTaskAndReply(task_runner, from_here, std::move(task), DoNothing());
- }
- CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
- TaskRunner* task_runner,
- const Location& from_here,
- OnceClosure task,
- OnceClosure reply) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- CHECK(weak_this_);
- // We need a SequencedTaskRunnerHandle to run |reply|.
- DCHECK(SequencedTaskRunnerHandle::IsSet());
- auto flag = MakeRefCounted<TaskCancellationFlag>();
- TaskId id = next_id_;
- next_id_++; // int64_t is big enough that we ignore the potential overflow.
- // Unretained(this) is safe because |flag| will have been set to the
- // "canceled" state after |this| is deleted.
- OnceClosure untrack_closure =
- BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
- bool success = task_runner->PostTaskAndReply(
- from_here,
- BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(), flag,
- std::move(task)),
- BindOnce(&RunThenUntrackIfNotCanceled, SequencedTaskRunnerHandle::Get(),
- flag, std::move(reply), std::move(untrack_closure)));
- if (!success)
- return kBadTaskId;
- Track(id, std::move(flag));
- return id;
- }
- CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
- IsCanceledCallback* is_canceled_cb) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- DCHECK(SequencedTaskRunnerHandle::IsSet());
- TaskId id = next_id_;
- next_id_++; // int64_t is big enough that we ignore the potential overflow.
- auto flag = MakeRefCounted<TaskCancellationFlag>();
- // Unretained(this) is safe because |flag| will have been set to the
- // "canceled" state after |this| is deleted.
- OnceClosure untrack_closure =
- BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
- // Will always run |untrack_closure| on current sequence.
- ScopedClosureRunner untrack_runner(
- BindOnce(&RunOrPostToTaskRunner, SequencedTaskRunnerHandle::Get(),
- BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(),
- flag, std::move(untrack_closure))));
- *is_canceled_cb = BindRepeating(&IsCanceled, SequencedTaskRunnerHandle::Get(),
- flag, std::move(untrack_runner));
- Track(id, std::move(flag));
- return id;
- }
- void CancelableTaskTracker::TryCancel(TaskId id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- const auto it = task_flags_.find(id);
- if (it == task_flags_.end()) {
- // Two possibilities:
- //
- // 1. The task has already been untracked.
- // 2. The TaskId is bad or unknown.
- //
- // Since this function is best-effort, it's OK to ignore these.
- return;
- }
- it->second->data.Set();
- // Remove |id| from |task_flags_| immediately, since we have no further
- // use for tracking it. This allows the reply closures (see
- // PostTaskAndReply()) for cancelled tasks to be skipped, since they have
- // no clean-up to perform.
- task_flags_.erase(it);
- }
- void CancelableTaskTracker::TryCancelAll() {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- for (const auto& it : task_flags_)
- it.second->data.Set();
- task_flags_.clear();
- }
- bool CancelableTaskTracker::HasTrackedTasks() const {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- return !task_flags_.empty();
- }
- // static
- void CancelableTaskTracker::RunIfNotCanceled(
- const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
- const scoped_refptr<TaskCancellationFlag>& flag,
- OnceClosure task) {
- // TODO(https://crbug.com/1009795): Record durations for executed tasks,
- // correlated with whether the task runs on a background or foreground
- // sequence, and whether it is the same sequence as the CancelableTaskTracker.
- // Also correlate with whether the task was run despite being canceled, to
- // allow an experiment to assess the value of off-sequence cancelation.
- // Record canceled & off-sequence status for all tasks.
- const bool was_canceled = flag->data.IsSet();
- const bool same_sequence = origin_task_runner->RunsTasksInCurrentSequence();
- const TaskStatus task_status =
- was_canceled ? (same_sequence ? TaskStatus::kSameSequenceCanceled
- : TaskStatus::kOffSequenceCanceled)
- : (same_sequence ? TaskStatus::kSameSequenceLive
- : TaskStatus::kOffSequenceLive);
- UMA_HISTOGRAM_ENUMERATION("Scheduler.CancelableTaskTracker.TaskStatus",
- task_status);
- // Skip tasks if they are canceled, taking into account the off-sequence
- // cancelation experiment.
- const bool skip_task =
- was_canceled && (AllowOffSequenceTaskCancelation() || same_sequence);
- if (skip_task)
- return;
- // Run the task and record its duration.
- const TimeTicks before_task_ticks = TimeTicks::Now();
- std::move(task).Run();
- const TimeDelta duration = TimeTicks::Now() - before_task_ticks;
- const bool is_background =
- internal::GetTaskPriorityForCurrentThread() < TaskPriority::USER_VISIBLE;
- UmaRecordTaskDuration(same_sequence, is_background, was_canceled, duration);
- }
- // static
- void CancelableTaskTracker::RunThenUntrackIfNotCanceled(
- const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
- const scoped_refptr<TaskCancellationFlag>& flag,
- OnceClosure task,
- OnceClosure untrack) {
- RunIfNotCanceled(origin_task_runner, flag, std::move(task));
- RunIfNotCanceled(origin_task_runner, flag, std::move(untrack));
- }
- // static
- bool CancelableTaskTracker::IsCanceled(
- const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
- const scoped_refptr<TaskCancellationFlag>& flag,
- const ScopedClosureRunner& cleanup_runner) {
- return flag->data.IsSet() &&
- (AllowOffSequenceTaskCancelation() ||
- origin_task_runner->RunsTasksInCurrentSequence());
- }
- void CancelableTaskTracker::Track(TaskId id,
- scoped_refptr<TaskCancellationFlag> flag) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- CHECK(weak_this_);
- bool success = task_flags_.insert(std::make_pair(id, std::move(flag))).second;
- DCHECK(success);
- }
- void CancelableTaskTracker::Untrack(TaskId id) {
- DCHECK(sequence_checker_.CalledOnValidSequence());
- CHECK(weak_this_);
- size_t num = task_flags_.erase(id);
- DCHECK_EQ(1u, num);
- }
- } // namespace base
|