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 <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