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