[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 5 | #include "base/task/cancelable_task_tracker.h" |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 6 | |
| 7 | #include <utility> |
| 8 | |
| 9 | #include "base/bind.h" |
[email protected] | 1c232c2 | 2013-08-30 02:04:04 | [diff] [blame] | 10 | #include "base/callback_helpers.h" |
[email protected] | 3d5b88a | 2012-11-28 23:28:45 | [diff] [blame] | 11 | #include "base/compiler_specific.h" |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 12 | #include "base/location.h" |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 13 | #include "base/memory/ref_counted.h" |
[email protected] | 76ae8a6 | 2013-05-10 05:34:22 | [diff] [blame] | 14 | #include "base/message_loop/message_loop_proxy.h" |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 15 | #include "base/synchronization/cancellation_flag.h" |
| 16 | #include "base/task_runner.h" |
| 17 | |
| 18 | using base::Bind; |
| 19 | using base::CancellationFlag; |
| 20 | using base::Closure; |
| 21 | using base::hash_map; |
| 22 | using base::TaskRunner; |
| 23 | |
| 24 | namespace { |
| 25 | |
| 26 | void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) { |
| 27 | if (!flag->IsSet()) |
| 28 | task.Run(); |
| 29 | } |
| 30 | |
| 31 | void RunIfNotCanceledThenUntrack(const CancellationFlag* flag, |
| 32 | const Closure& task, |
| 33 | const Closure& untrack) { |
| 34 | RunIfNotCanceled(flag, task); |
| 35 | untrack.Run(); |
| 36 | } |
| 37 | |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 38 | bool IsCanceled(const CancellationFlag* flag, |
| 39 | base::ScopedClosureRunner* cleanup_runner) { |
| 40 | return flag->IsSet(); |
| 41 | } |
| 42 | |
| 43 | void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) { |
| 44 | closure.Run(); |
| 45 | delete flag; |
| 46 | } |
| 47 | |
| 48 | void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) { |
| 49 | if (task_runner->RunsTasksOnCurrentThread()) |
| 50 | closure.Run(); |
| 51 | else |
| 52 | task_runner->PostTask(FROM_HERE, closure); |
| 53 | } |
| 54 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 55 | } // namespace |
| 56 | |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 57 | namespace base { |
| 58 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 59 | // static |
| 60 | const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; |
| 61 | |
| 62 | CancelableTaskTracker::CancelableTaskTracker() |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 63 | : weak_factory_(this), next_id_(1) {} |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 64 | |
| 65 | CancelableTaskTracker::~CancelableTaskTracker() { |
[email protected] | d5dd90d | 2012-11-26 21:13:35 | [diff] [blame] | 66 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 67 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 68 | TryCancelAll(); |
| 69 | } |
| 70 | |
| 71 | CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( |
| 72 | TaskRunner* task_runner, |
| 73 | const tracked_objects::Location& from_here, |
| 74 | const Closure& task) { |
[email protected] | d5dd90d | 2012-11-26 21:13:35 | [diff] [blame] | 75 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 76 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 77 | return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing)); |
| 78 | } |
| 79 | |
| 80 | CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( |
| 81 | TaskRunner* task_runner, |
| 82 | const tracked_objects::Location& from_here, |
| 83 | const Closure& task, |
| 84 | const Closure& reply) { |
| 85 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 86 | |
| 87 | // We need a MessageLoop to run reply. |
[email protected] | cadac62 | 2013-06-11 16:46:36 | [diff] [blame] | 88 | DCHECK(base::MessageLoopProxy::current().get()); |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 89 | |
| 90 | // Owned by reply callback below. |
| 91 | CancellationFlag* flag = new CancellationFlag(); |
| 92 | |
| 93 | TaskId id = next_id_; |
| 94 | next_id_++; // int64 is big enough that we ignore the potential overflow. |
| 95 | |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 96 | const Closure& untrack_closure = |
| 97 | Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id); |
| 98 | bool success = |
| 99 | task_runner->PostTaskAndReply(from_here, |
| 100 | Bind(&RunIfNotCanceled, flag, task), |
| 101 | Bind(&RunIfNotCanceledThenUntrack, |
| 102 | base::Owned(flag), |
| 103 | reply, |
| 104 | untrack_closure)); |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 105 | |
| 106 | if (!success) |
| 107 | return kBadTaskId; |
| 108 | |
| 109 | Track(id, flag); |
| 110 | return id; |
| 111 | } |
| 112 | |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 113 | CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( |
| 114 | IsCanceledCallback* is_canceled_cb) { |
| 115 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | cadac62 | 2013-06-11 16:46:36 | [diff] [blame] | 116 | DCHECK(base::MessageLoopProxy::current().get()); |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 117 | |
| 118 | TaskId id = next_id_; |
| 119 | next_id_++; // int64 is big enough that we ignore the potential overflow. |
| 120 | |
| 121 | // Will be deleted by |untrack_and_delete_flag| after Untrack(). |
| 122 | CancellationFlag* flag = new CancellationFlag(); |
| 123 | |
| 124 | Closure untrack_and_delete_flag = Bind( |
| 125 | &RunAndDeleteFlag, |
| 126 | Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id), |
| 127 | flag); |
| 128 | |
| 129 | // Will always run |untrack_and_delete_flag| on current MessageLoop. |
| 130 | base::ScopedClosureRunner* untrack_and_delete_flag_runner = |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 131 | new base::ScopedClosureRunner(Bind(&RunOrPostToTaskRunner, |
| 132 | base::MessageLoopProxy::current(), |
| 133 | untrack_and_delete_flag)); |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 134 | |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 135 | *is_canceled_cb = |
| 136 | Bind(&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner)); |
[email protected] | 4f1935e | 2012-11-16 23:10:07 | [diff] [blame] | 137 | |
| 138 | Track(id, flag); |
| 139 | return id; |
| 140 | } |
| 141 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 142 | void CancelableTaskTracker::TryCancel(TaskId id) { |
| 143 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 144 | |
| 145 | hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id); |
| 146 | if (it == task_flags_.end()) { |
[email protected] | 3d5b88a | 2012-11-28 23:28:45 | [diff] [blame] | 147 | // Two possibilities: |
| 148 | // |
| 149 | // 1. The task has already been untracked. |
| 150 | // 2. The TaskId is bad or unknown. |
| 151 | // |
| 152 | // Since this function is best-effort, it's OK to ignore these. |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 153 | return; |
| 154 | } |
| 155 | it->second->Set(); |
| 156 | } |
| 157 | |
| 158 | void CancelableTaskTracker::TryCancelAll() { |
| 159 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 160 | |
| 161 | for (hash_map<TaskId, CancellationFlag*>::const_iterator it = |
| 162 | task_flags_.begin(); |
| 163 | it != task_flags_.end(); |
| 164 | ++it) { |
| 165 | it->second->Set(); |
| 166 | } |
| 167 | } |
| 168 | |
[email protected] | 3d5b88a | 2012-11-28 23:28:45 | [diff] [blame] | 169 | bool CancelableTaskTracker::HasTrackedTasks() const { |
| 170 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 171 | return !task_flags_.empty(); |
| 172 | } |
| 173 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 174 | void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { |
[email protected] | d5dd90d | 2012-11-26 21:13:35 | [diff] [blame] | 175 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 176 | |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 177 | bool success = task_flags_.insert(std::make_pair(id, flag)).second; |
| 178 | DCHECK(success); |
| 179 | } |
| 180 | |
| 181 | void CancelableTaskTracker::Untrack(TaskId id) { |
| 182 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 62867b0 | 2012-11-03 07:21:03 | [diff] [blame] | 183 | size_t num = task_flags_.erase(id); |
| 184 | DCHECK_EQ(1u, num); |
| 185 | } |
[email protected] | e95b717f | 2014-02-06 13:47:13 | [diff] [blame] | 186 | |
| 187 | } // namespace base |