xref: /aosp_15_r20/external/cronet/base/task/cancelable_task_tracker.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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