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