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 #include "base/task/cancelable_task_tracker.h" 6 7 #include <utility> 8 9 #include "base/bind.h" 10 #include "base/callback_helpers.h" 11 #include "base/compiler_specific.h" 12 #include "base/location.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/message_loop/message_loop_proxy.h" 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 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 55 } // namespace 56 57 namespace base { 58 59 // static 60 const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; 61 62 CancelableTaskTracker::CancelableTaskTracker() 63 : weak_factory_(this), next_id_(1) {} 64 65 CancelableTaskTracker::~CancelableTaskTracker() { 66 DCHECK(thread_checker_.CalledOnValidThread()); 67 68 TryCancelAll(); 69 } 70 71 CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( 72 TaskRunner* task_runner, 73 const tracked_objects::Location& from_here, 74 const Closure& task) { 75 DCHECK(thread_checker_.CalledOnValidThread()); 76 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. 88 DCHECK(base::MessageLoopProxy::current().get()); 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 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)); 105 106 if (!success) 107 return kBadTaskId; 108 109 Track(id, flag); 110 return id; 111 } 112 113 CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( 114 IsCanceledCallback* is_canceled_cb) { 115 DCHECK(thread_checker_.CalledOnValidThread()); 116 DCHECK(base::MessageLoopProxy::current().get()); 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 = 131 new base::ScopedClosureRunner(Bind(&RunOrPostToTaskRunner, 132 base::MessageLoopProxy::current(), 133 untrack_and_delete_flag)); 134 135 *is_canceled_cb = 136 Bind(&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner)); 137 138 Track(id, flag); 139 return id; 140 } 141 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()) { 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. 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 169 bool CancelableTaskTracker::HasTrackedTasks() const { 170 DCHECK(thread_checker_.CalledOnValidThread()); 171 return !task_flags_.empty(); 172 } 173 174 void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { 175 DCHECK(thread_checker_.CalledOnValidThread()); 176 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()); 183 size_t num = task_flags_.erase(id); 184 DCHECK_EQ(1u, num); 185 } 186 187 } // namespace base 188