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