cancelable_task_tracker.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/task/cancelable_task_tracker.h"
  5. #include <stddef.h>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/callback_helpers.h"
  9. #include "base/feature_list.h"
  10. #include "base/location.h"
  11. #include "base/memory/ref_counted.h"
  12. #include "base/metrics/histogram_macros.h"
  13. #include "base/task/scoped_set_task_priority_for_current_thread.h"
  14. #include "base/task/sequenced_task_runner.h"
  15. #include "base/task/task_runner.h"
  16. #include "base/threading/sequenced_task_runner_handle.h"
  17. namespace base {
  18. namespace {
  19. void RunOrPostToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
  20. OnceClosure closure) {
  21. if (task_runner->RunsTasksInCurrentSequence())
  22. std::move(closure).Run();
  23. else
  24. task_runner->PostTask(FROM_HERE, std::move(closure));
  25. }
  26. // TODO(https://crbug.com/1009795): Remove these once we have established
  27. // whether off-sequence cancelation is worthwhile.
  28. // These values are persisted to logs. Entries should not be renumbered and
  29. // numeric values should never be reused.
  30. enum class TaskStatus {
  31. kSameSequenceLive = 0,
  32. kOffSequenceLive = 1,
  33. kSameSequenceCanceled = 2,
  34. kOffSequenceCanceled = 3,
  35. kMaxValue = kOffSequenceCanceled,
  36. };
  37. void UmaRecordTaskDuration(bool same_sequence,
  38. bool background,
  39. bool canceled,
  40. TimeDelta duration) {
  41. #define DECLARE_HISTOGRAM(suffix) \
  42. Histogram::FactoryTimeGet( \
  43. "Scheduler.CancelableTaskTracker.TaskDuration2_" suffix, \
  44. Milliseconds(1), Seconds(10), 50, Histogram::kUmaTargetedHistogramFlag)
  45. static HistogramBase* histograms[] = {
  46. DECLARE_HISTOGRAM("LiveForegroundOffSequence"),
  47. DECLARE_HISTOGRAM("LiveForegroundSameSequence"),
  48. DECLARE_HISTOGRAM("LiveBackgroundOffSequence"),
  49. DECLARE_HISTOGRAM("LiveBackgroundSameSequence"),
  50. DECLARE_HISTOGRAM("CanceledForegroundOffSequence"),
  51. DECLARE_HISTOGRAM("CanceledForegroundSameSequence"),
  52. DECLARE_HISTOGRAM("CanceledBackgroundOffSequence"),
  53. DECLARE_HISTOGRAM("CanceledBackgroundSameSequence")};
  54. int i = (same_sequence ? 1 : 0) + (background ? 2 : 0) + (canceled ? 4 : 0);
  55. histograms[i]->AddTimeMillisecondsGranularity(duration);
  56. }
  57. const base::Feature kAllowOffSequenceTaskCancelation{
  58. "AllowOffSequenceTaskCancelation", base::FEATURE_ENABLED_BY_DEFAULT};
  59. bool AllowOffSequenceTaskCancelation() {
  60. if (!base::FeatureList::GetInstance())
  61. return true;
  62. return base::FeatureList::IsEnabled(kAllowOffSequenceTaskCancelation);
  63. }
  64. } // namespace
  65. // static
  66. const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
  67. CancelableTaskTracker::CancelableTaskTracker() {
  68. weak_this_ = weak_factory_.GetWeakPtr();
  69. }
  70. CancelableTaskTracker::~CancelableTaskTracker() {
  71. DCHECK(sequence_checker_.CalledOnValidSequence());
  72. TryCancelAll();
  73. }
  74. CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
  75. TaskRunner* task_runner,
  76. const Location& from_here,
  77. OnceClosure task) {
  78. DCHECK(sequence_checker_.CalledOnValidSequence());
  79. CHECK(weak_this_);
  80. return PostTaskAndReply(task_runner, from_here, std::move(task), DoNothing());
  81. }
  82. CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
  83. TaskRunner* task_runner,
  84. const Location& from_here,
  85. OnceClosure task,
  86. OnceClosure reply) {
  87. DCHECK(sequence_checker_.CalledOnValidSequence());
  88. CHECK(weak_this_);
  89. // We need a SequencedTaskRunnerHandle to run |reply|.
  90. DCHECK(SequencedTaskRunnerHandle::IsSet());
  91. auto flag = MakeRefCounted<TaskCancellationFlag>();
  92. TaskId id = next_id_;
  93. next_id_++; // int64_t is big enough that we ignore the potential overflow.
  94. // Unretained(this) is safe because |flag| will have been set to the
  95. // "canceled" state after |this| is deleted.
  96. OnceClosure untrack_closure =
  97. BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
  98. bool success = task_runner->PostTaskAndReply(
  99. from_here,
  100. BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(), flag,
  101. std::move(task)),
  102. BindOnce(&RunThenUntrackIfNotCanceled, SequencedTaskRunnerHandle::Get(),
  103. flag, std::move(reply), std::move(untrack_closure)));
  104. if (!success)
  105. return kBadTaskId;
  106. Track(id, std::move(flag));
  107. return id;
  108. }
  109. CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
  110. IsCanceledCallback* is_canceled_cb) {
  111. DCHECK(sequence_checker_.CalledOnValidSequence());
  112. DCHECK(SequencedTaskRunnerHandle::IsSet());
  113. TaskId id = next_id_;
  114. next_id_++; // int64_t is big enough that we ignore the potential overflow.
  115. auto flag = MakeRefCounted<TaskCancellationFlag>();
  116. // Unretained(this) is safe because |flag| will have been set to the
  117. // "canceled" state after |this| is deleted.
  118. OnceClosure untrack_closure =
  119. BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
  120. // Will always run |untrack_closure| on current sequence.
  121. ScopedClosureRunner untrack_runner(
  122. BindOnce(&RunOrPostToTaskRunner, SequencedTaskRunnerHandle::Get(),
  123. BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(),
  124. flag, std::move(untrack_closure))));
  125. *is_canceled_cb = BindRepeating(&IsCanceled, SequencedTaskRunnerHandle::Get(),
  126. flag, std::move(untrack_runner));
  127. Track(id, std::move(flag));
  128. return id;
  129. }
  130. void CancelableTaskTracker::TryCancel(TaskId id) {
  131. DCHECK(sequence_checker_.CalledOnValidSequence());
  132. const auto it = task_flags_.find(id);
  133. if (it == task_flags_.end()) {
  134. // Two possibilities:
  135. //
  136. // 1. The task has already been untracked.
  137. // 2. The TaskId is bad or unknown.
  138. //
  139. // Since this function is best-effort, it's OK to ignore these.
  140. return;
  141. }
  142. it->second->data.Set();
  143. // Remove |id| from |task_flags_| immediately, since we have no further
  144. // use for tracking it. This allows the reply closures (see
  145. // PostTaskAndReply()) for cancelled tasks to be skipped, since they have
  146. // no clean-up to perform.
  147. task_flags_.erase(it);
  148. }
  149. void CancelableTaskTracker::TryCancelAll() {
  150. DCHECK(sequence_checker_.CalledOnValidSequence());
  151. for (const auto& it : task_flags_)
  152. it.second->data.Set();
  153. task_flags_.clear();
  154. }
  155. bool CancelableTaskTracker::HasTrackedTasks() const {
  156. DCHECK(sequence_checker_.CalledOnValidSequence());
  157. return !task_flags_.empty();
  158. }
  159. // static
  160. void CancelableTaskTracker::RunIfNotCanceled(
  161. const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
  162. const scoped_refptr<TaskCancellationFlag>& flag,
  163. OnceClosure task) {
  164. // TODO(https://crbug.com/1009795): Record durations for executed tasks,
  165. // correlated with whether the task runs on a background or foreground
  166. // sequence, and whether it is the same sequence as the CancelableTaskTracker.
  167. // Also correlate with whether the task was run despite being canceled, to
  168. // allow an experiment to assess the value of off-sequence cancelation.
  169. // Record canceled & off-sequence status for all tasks.
  170. const bool was_canceled = flag->data.IsSet();
  171. const bool same_sequence = origin_task_runner->RunsTasksInCurrentSequence();
  172. const TaskStatus task_status =
  173. was_canceled ? (same_sequence ? TaskStatus::kSameSequenceCanceled
  174. : TaskStatus::kOffSequenceCanceled)
  175. : (same_sequence ? TaskStatus::kSameSequenceLive
  176. : TaskStatus::kOffSequenceLive);
  177. UMA_HISTOGRAM_ENUMERATION("Scheduler.CancelableTaskTracker.TaskStatus",
  178. task_status);
  179. // Skip tasks if they are canceled, taking into account the off-sequence
  180. // cancelation experiment.
  181. const bool skip_task =
  182. was_canceled && (AllowOffSequenceTaskCancelation() || same_sequence);
  183. if (skip_task)
  184. return;
  185. // Run the task and record its duration.
  186. const TimeTicks before_task_ticks = TimeTicks::Now();
  187. std::move(task).Run();
  188. const TimeDelta duration = TimeTicks::Now() - before_task_ticks;
  189. const bool is_background =
  190. internal::GetTaskPriorityForCurrentThread() < TaskPriority::USER_VISIBLE;
  191. UmaRecordTaskDuration(same_sequence, is_background, was_canceled, duration);
  192. }
  193. // static
  194. void CancelableTaskTracker::RunThenUntrackIfNotCanceled(
  195. const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
  196. const scoped_refptr<TaskCancellationFlag>& flag,
  197. OnceClosure task,
  198. OnceClosure untrack) {
  199. RunIfNotCanceled(origin_task_runner, flag, std::move(task));
  200. RunIfNotCanceled(origin_task_runner, flag, std::move(untrack));
  201. }
  202. // static
  203. bool CancelableTaskTracker::IsCanceled(
  204. const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
  205. const scoped_refptr<TaskCancellationFlag>& flag,
  206. const ScopedClosureRunner& cleanup_runner) {
  207. return flag->data.IsSet() &&
  208. (AllowOffSequenceTaskCancelation() ||
  209. origin_task_runner->RunsTasksInCurrentSequence());
  210. }
  211. void CancelableTaskTracker::Track(TaskId id,
  212. scoped_refptr<TaskCancellationFlag> flag) {
  213. DCHECK(sequence_checker_.CalledOnValidSequence());
  214. CHECK(weak_this_);
  215. bool success = task_flags_.insert(std::make_pair(id, std::move(flag))).second;
  216. DCHECK(success);
  217. }
  218. void CancelableTaskTracker::Untrack(TaskId id) {
  219. DCHECK(sequence_checker_.CalledOnValidSequence());
  220. CHECK(weak_this_);
  221. size_t num = task_flags_.erase(id);
  222. DCHECK_EQ(1u, num);
  223. }
  224. } // namespace base