1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task/cancelable_task_tracker.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/task/sequenced_task_runner.h"
17 #include "base/task/task_runner.h"
18
19 namespace base {
20
21 namespace {
22
RunOrPostToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,OnceClosure closure)23 void RunOrPostToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
24 OnceClosure closure) {
25 if (task_runner->RunsTasksInCurrentSequence())
26 std::move(closure).Run();
27 else
28 task_runner->PostTask(FROM_HERE, std::move(closure));
29 }
30
31 } // namespace
32
33 // static
34 const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
35
CancelableTaskTracker()36 CancelableTaskTracker::CancelableTaskTracker() {
37 weak_this_ = weak_factory_.GetWeakPtr();
38 }
39
~CancelableTaskTracker()40 CancelableTaskTracker::~CancelableTaskTracker() {
41 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
42
43 TryCancelAll();
44 }
45
PostTask(TaskRunner * task_runner,const Location & from_here,OnceClosure task)46 CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
47 TaskRunner* task_runner,
48 const Location& from_here,
49 OnceClosure task) {
50 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
51 CHECK(weak_this_);
52
53 return PostTaskAndReply(task_runner, from_here, std::move(task), DoNothing());
54 }
55
PostTaskAndReply(TaskRunner * task_runner,const Location & from_here,OnceClosure task,OnceClosure reply)56 CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
57 TaskRunner* task_runner,
58 const Location& from_here,
59 OnceClosure task,
60 OnceClosure reply) {
61 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
62 CHECK(weak_this_);
63
64 // We need a SequencedTaskRunner::CurrentDefaultHandle to run |reply|.
65 DCHECK(SequencedTaskRunner::HasCurrentDefault());
66
67 auto flag = MakeRefCounted<TaskCancellationFlag>();
68
69 TaskId id = next_id_;
70 next_id_++; // int64_t is big enough that we ignore the potential overflow.
71
72 // Unretained(this) is safe because |flag| will have been set to the
73 // "canceled" state after |this| is deleted.
74 OnceClosure untrack_closure =
75 BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
76 bool success = task_runner->PostTaskAndReply(
77 from_here, BindOnce(&RunIfNotCanceled, flag, std::move(task)),
78 BindOnce(&RunThenUntrackIfNotCanceled, flag, std::move(reply),
79 std::move(untrack_closure)));
80
81 if (!success)
82 return kBadTaskId;
83
84 Track(id, std::move(flag));
85 return id;
86 }
87
NewTrackedTaskId(IsCanceledCallback * is_canceled_cb)88 CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
89 IsCanceledCallback* is_canceled_cb) {
90 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
91 DCHECK(SequencedTaskRunner::HasCurrentDefault());
92
93 TaskId id = next_id_;
94 next_id_++; // int64_t is big enough that we ignore the potential overflow.
95
96 auto flag = MakeRefCounted<TaskCancellationFlag>();
97
98 // Unretained(this) is safe because |flag| will have been set to the
99 // "canceled" state after |this| is deleted.
100 OnceClosure untrack_closure =
101 BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id);
102
103 // Will always run |untrack_closure| on current sequence.
104 ScopedClosureRunner untrack_runner(
105 BindOnce(&RunOrPostToTaskRunner, SequencedTaskRunner::GetCurrentDefault(),
106 BindOnce(&RunIfNotCanceled, flag, std::move(untrack_closure))));
107
108 *is_canceled_cb = BindRepeating(&IsCanceled, flag, std::move(untrack_runner));
109
110 Track(id, std::move(flag));
111 return id;
112 }
113
TryCancel(TaskId id)114 void CancelableTaskTracker::TryCancel(TaskId id) {
115 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
116
117 const auto it = task_flags_.find(id);
118 if (it == task_flags_.end()) {
119 // Two possibilities:
120 //
121 // 1. The task has already been untracked.
122 // 2. The TaskId is bad or unknown.
123 //
124 // Since this function is best-effort, it's OK to ignore these.
125 return;
126 }
127 it->second->data.Set();
128
129 // Remove |id| from |task_flags_| immediately, since we have no further
130 // use for tracking it. This allows the reply closures (see
131 // PostTaskAndReply()) for cancelled tasks to be skipped, since they have
132 // no clean-up to perform.
133 task_flags_.erase(it);
134 }
135
TryCancelAll()136 void CancelableTaskTracker::TryCancelAll() {
137 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
138 for (const auto& it : task_flags_)
139 it.second->data.Set();
140 task_flags_.clear();
141 }
142
HasTrackedTasks() const143 bool CancelableTaskTracker::HasTrackedTasks() const {
144 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
145 return !task_flags_.empty();
146 }
147
148 // static
RunIfNotCanceled(const scoped_refptr<TaskCancellationFlag> & flag,OnceClosure task)149 void CancelableTaskTracker::RunIfNotCanceled(
150 const scoped_refptr<TaskCancellationFlag>& flag,
151 OnceClosure task) {
152 if (!flag->data.IsSet()) {
153 std::move(task).Run();
154 }
155 }
156
157 // static
RunThenUntrackIfNotCanceled(const scoped_refptr<TaskCancellationFlag> & flag,OnceClosure task,OnceClosure untrack)158 void CancelableTaskTracker::RunThenUntrackIfNotCanceled(
159 const scoped_refptr<TaskCancellationFlag>& flag,
160 OnceClosure task,
161 OnceClosure untrack) {
162 RunIfNotCanceled(flag, std::move(task));
163 RunIfNotCanceled(flag, std::move(untrack));
164 }
165
166 // static
IsCanceled(const scoped_refptr<TaskCancellationFlag> & flag,const ScopedClosureRunner & cleanup_runner)167 bool CancelableTaskTracker::IsCanceled(
168 const scoped_refptr<TaskCancellationFlag>& flag,
169 const ScopedClosureRunner& cleanup_runner) {
170 return flag->data.IsSet();
171 }
172
Track(TaskId id,scoped_refptr<TaskCancellationFlag> flag)173 void CancelableTaskTracker::Track(TaskId id,
174 scoped_refptr<TaskCancellationFlag> flag) {
175 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
176 CHECK(weak_this_);
177 bool success = task_flags_.insert(std::make_pair(id, std::move(flag))).second;
178 DCHECK(success);
179 }
180
Untrack(TaskId id)181 void CancelableTaskTracker::Untrack(TaskId id) {
182 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
183 CHECK(weak_this_);
184 size_t num = task_flags_.erase(id);
185 DCHECK_EQ(1u, num);
186 }
187
188 } // namespace base
189