1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
base / task / cancelable_task_tracker.h [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// CancelableTaskTracker posts tasks (in the form of a OnceClosure) to a
// TaskRunner, and is able to cancel the task later if it's not needed
// anymore. On destruction, CancelableTaskTracker will cancel all
// tracked tasks.
//
// Each cancelable task can be associated with a reply (also a OnceClosure).
// After the task is run on the TaskRunner, |reply| will be posted back to
// originating TaskRunner.
//
// NOTE:
//
// CancelableOnceCallback (base/cancelable_callback.h) and WeakPtr binding are
// preferred solutions for canceling a task. However, they don't support
// cancelation from another sequence. This is sometimes a performance critical
// requirement. E.g. We need to cancel database lookup task on DB thread when
// user changes inputted text. If it is performance critical to do a best effort
// cancelation of a task, then CancelableTaskTracker is appropriate, otherwise
// use one of the other mechanisms.
//
// THREAD-SAFETY:
//
// 1. A CancelableTaskTracker object must be created, used, and destroyed on a
// single sequence.
//
// 2. It's safe to destroy a CancelableTaskTracker while there are outstanding
// tasks. This is commonly used to cancel all outstanding tasks.
//
// 3. The task is deleted on the target sequence, and the reply are deleted on
// the originating sequence.
//
// 4. IsCanceledCallback can be run or deleted on any sequence.
#ifndef BASE_TASK_CANCELABLE_TASK_TRACKER_H_
#define BASE_TASK_CANCELABLE_TASK_TRACKER_H_
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/base_export.h"
#include "base/containers/small_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/synchronization/atomic_flag.h"
#include "base/task/post_task_and_reply_with_result_internal.h"
namespace base {
class Location;
class ScopedClosureRunner;
class TaskRunner;
class BASE_EXPORT CancelableTaskTracker {
public:
// All values except kBadTaskId are valid.
typedef int64_t TaskId;
static const TaskId kBadTaskId;
using IsCanceledCallback = RepeatingCallback<bool()>;
CancelableTaskTracker();
CancelableTaskTracker(const CancelableTaskTracker&) = delete;
CancelableTaskTracker& operator=(const CancelableTaskTracker&) = delete;
// Cancels all tracked tasks.
~CancelableTaskTracker();
TaskId PostTask(TaskRunner* task_runner,
const Location& from_here,
OnceClosure task);
TaskId PostTaskAndReply(TaskRunner* task_runner,
const Location& from_here,
OnceClosure task,
OnceClosure reply);
template <typename TaskReturnType, typename ReplyArgType>
TaskId PostTaskAndReplyWithResult(TaskRunner* task_runner,
const Location& from_here,
OnceCallback<TaskReturnType()> task,
OnceCallback<void(ReplyArgType)> reply) {
auto* result = new std::unique_ptr<TaskReturnType>();
return PostTaskAndReply(
task_runner, from_here,
BindOnce(&internal::ReturnAsParamAdapter<TaskReturnType>,
std::move(task), Unretained(result)),
BindOnce(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
std::move(reply), Owned(result)));
}
// Creates a tracked TaskId and an associated IsCanceledCallback. Client can
// later call TryCancel() with the returned TaskId, and run |is_canceled_cb|
// from any thread to check whether the TaskId is canceled.
//
// The returned task ID is tracked until the last copy of
// |is_canceled_cb| is destroyed.
//
// Note. This function is used to address some special cancelation requirement
// in existing code. You SHOULD NOT need this function in new code.
TaskId NewTrackedTaskId(IsCanceledCallback* is_canceled_cb);
// After calling this function, |task| and |reply| will not run. If the
// cancelation happens when |task| is running or has finished running, |reply|
// will not run. If |reply| is running or has finished running, cancellation
// is a noop.
//
// Note. It's OK to cancel a |task| for more than once. The later calls are
// noops.
void TryCancel(TaskId id);
// It's OK to call this function for more than once. The later calls are
// noops.
void TryCancelAll();
// Returns true iff there are in-flight tasks that are still being
// tracked.
bool HasTrackedTasks() const;
private:
// Cancellation flags are ref-counted to ensure they remain valid even if the
// tracker and its calling thread are torn down while there are still
// cancelable tasks queued to the target TaskRunner.
// See https://crbug.com/918948.
using TaskCancellationFlag = RefCountedData<AtomicFlag>;
static void RunIfNotCanceled(const scoped_refptr<TaskCancellationFlag>& flag,
OnceClosure task);
static void RunThenUntrackIfNotCanceled(
const scoped_refptr<TaskCancellationFlag>& flag,
OnceClosure task,
OnceClosure untrack);
static bool IsCanceled(const scoped_refptr<TaskCancellationFlag>& flag,
const ScopedClosureRunner& cleanup_runner);
void Track(TaskId id, scoped_refptr<TaskCancellationFlag> flag);
void Untrack(TaskId id);
// Typically the number of tasks are 0-2 and occationally 3-4. But since
// this is a general API that could be used in unexpected ways, use a
// small_map instead of a flat_map to avoid falling over if there are many
// tasks.
small_map<std::map<TaskId, scoped_refptr<TaskCancellationFlag>>, 4>
task_flags_;
TaskId next_id_ = 1;
SEQUENCE_CHECKER(sequence_checker_);
// TODO(crbug.com/40050290): Remove once crasher is resolved.
base::WeakPtr<CancelableTaskTracker> weak_this_;
base::WeakPtrFactory<CancelableTaskTracker> weak_factory_{this};
};
} // namespace base
#endif // BASE_TASK_CANCELABLE_TASK_TRACKER_H_