Home | History | Annotate | Download | only in common
      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