Home | History | Annotate | Download | only in task
      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 
     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 namespace base {
     58 
     59 // static
     60 const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
     61 
     62 CancelableTaskTracker::CancelableTaskTracker()
     63     : weak_factory_(this), next_id_(1) {}
     64 
     65 CancelableTaskTracker::~CancelableTaskTracker() {
     66   DCHECK(thread_checker_.CalledOnValidThread());
     67 
     68   TryCancelAll();
     69 }
     70 
     71 CancelableTaskTracker::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 
     80 CancelableTaskTracker::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 
    113 CancelableTaskTracker::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 
    142 void 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 
    158 void 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 
    169 bool CancelableTaskTracker::HasTrackedTasks() const {
    170   DCHECK(thread_checker_.CalledOnValidThread());
    171   return !task_flags_.empty();
    172 }
    173 
    174 void 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 
    181 void 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