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