1 // Copyright (c) 2012 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 "chrome/common/cancelable_task_tracker.h" 6 7 #include <utility> 8 9 #include "base/bind.h" 10 #include "base/bind_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 // static 58 const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; 59 60 CancelableTaskTracker::CancelableTaskTracker() 61 : weak_factory_(this), 62 next_id_(1) {} 63 64 CancelableTaskTracker::~CancelableTaskTracker() { 65 DCHECK(thread_checker_.CalledOnValidThread()); 66 67 TryCancelAll(); 68 } 69 70 CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( 71 TaskRunner* task_runner, 72 const tracked_objects::Location& from_here, 73 const Closure& task) { 74 DCHECK(thread_checker_.CalledOnValidThread()); 75 76 return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing)); 77 } 78 79 CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( 80 TaskRunner* task_runner, 81 const tracked_objects::Location& from_here, 82 const Closure& task, 83 const Closure& reply) { 84 DCHECK(thread_checker_.CalledOnValidThread()); 85 86 // We need a MessageLoop to run reply. 87 DCHECK(base::MessageLoopProxy::current().get()); 88 89 // Owned by reply callback below. 90 CancellationFlag* flag = new CancellationFlag(); 91 92 TaskId id = next_id_; 93 next_id_++; // int64 is big enough that we ignore the potential overflow. 94 95 const Closure& untrack_closure = Bind( 96 &CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id); 97 bool success = task_runner->PostTaskAndReply( 98 from_here, 99 Bind(&RunIfNotCanceled, flag, task), 100 Bind(&RunIfNotCanceledThenUntrack, 101 base::Owned(flag), reply, untrack_closure)); 102 103 if (!success) 104 return kBadTaskId; 105 106 Track(id, flag); 107 return id; 108 } 109 110 CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( 111 IsCanceledCallback* is_canceled_cb) { 112 DCHECK(thread_checker_.CalledOnValidThread()); 113 DCHECK(base::MessageLoopProxy::current().get()); 114 115 TaskId id = next_id_; 116 next_id_++; // int64 is big enough that we ignore the potential overflow. 117 118 // Will be deleted by |untrack_and_delete_flag| after Untrack(). 119 CancellationFlag* flag = new CancellationFlag(); 120 121 Closure untrack_and_delete_flag = Bind( 122 &RunAndDeleteFlag, 123 Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id), 124 flag); 125 126 // Will always run |untrack_and_delete_flag| on current MessageLoop. 127 base::ScopedClosureRunner* untrack_and_delete_flag_runner = 128 new base::ScopedClosureRunner( 129 Bind(&RunOrPostToTaskRunner, 130 base::MessageLoopProxy::current(), 131 untrack_and_delete_flag)); 132 133 *is_canceled_cb = Bind( 134 &IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner)); 135 136 Track(id, flag); 137 return id; 138 } 139 140 void CancelableTaskTracker::TryCancel(TaskId id) { 141 DCHECK(thread_checker_.CalledOnValidThread()); 142 143 hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id); 144 if (it == task_flags_.end()) { 145 // Two possibilities: 146 // 147 // 1. The task has already been untracked. 148 // 2. The TaskId is bad or unknown. 149 // 150 // Since this function is best-effort, it's OK to ignore these. 151 return; 152 } 153 it->second->Set(); 154 } 155 156 void CancelableTaskTracker::TryCancelAll() { 157 DCHECK(thread_checker_.CalledOnValidThread()); 158 159 for (hash_map<TaskId, CancellationFlag*>::const_iterator it = 160 task_flags_.begin(); 161 it != task_flags_.end(); 162 ++it) { 163 it->second->Set(); 164 } 165 } 166 167 bool CancelableTaskTracker::HasTrackedTasks() const { 168 DCHECK(thread_checker_.CalledOnValidThread()); 169 return !task_flags_.empty(); 170 } 171 172 void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { 173 DCHECK(thread_checker_.CalledOnValidThread()); 174 175 bool success = task_flags_.insert(std::make_pair(id, flag)).second; 176 DCHECK(success); 177 } 178 179 void CancelableTaskTracker::Untrack(TaskId id) { 180 DCHECK(thread_checker_.CalledOnValidThread()); 181 size_t num = task_flags_.erase(id); 182 DCHECK_EQ(1u, num); 183 } 184