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