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 18using base::Bind; 19using base::CancellationFlag; 20using base::Closure; 21using base::hash_map; 22using base::TaskRunner; 23 24namespace { 25 26void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) { 27 if (!flag->IsSet()) 28 task.Run(); 29} 30 31void RunIfNotCanceledThenUntrack(const CancellationFlag* flag, 32 const Closure& task, 33 const Closure& untrack) { 34 RunIfNotCanceled(flag, task); 35 untrack.Run(); 36} 37 38bool IsCanceled(const CancellationFlag* flag, 39 base::ScopedClosureRunner* cleanup_runner) { 40 return flag->IsSet(); 41} 42 43void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) { 44 closure.Run(); 45 delete flag; 46} 47 48void 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 57namespace base { 58 59// static 60const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; 61 62CancelableTaskTracker::CancelableTaskTracker() 63 : next_id_(1),weak_factory_(this) {} 64 65CancelableTaskTracker::~CancelableTaskTracker() { 66 DCHECK(thread_checker_.CalledOnValidThread()); 67 68 TryCancelAll(); 69} 70 71CancelableTaskTracker::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 80CancelableTaskTracker::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 113CancelableTaskTracker::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 142void 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 158void 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 169bool CancelableTaskTracker::HasTrackedTasks() const { 170 DCHECK(thread_checker_.CalledOnValidThread()); 171 return !task_flags_.empty(); 172} 173 174void 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 181void 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