|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/task/cancelable_task_tracker.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/callback_helpers.h" | 
|  | #include "base/location.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/synchronization/cancellation_flag.h" | 
|  | #include "base/task_runner.h" | 
|  | #include "base/threading/sequenced_task_runner_handle.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void RunIfNotCanceled(const CancellationFlag* flag, OnceClosure task) { | 
|  | if (!flag->IsSet()) | 
|  | std::move(task).Run(); | 
|  | } | 
|  |  | 
|  | void RunIfNotCanceledThenUntrack(const CancellationFlag* flag, | 
|  | OnceClosure task, | 
|  | OnceClosure untrack) { | 
|  | RunIfNotCanceled(flag, std::move(task)); | 
|  | std::move(untrack).Run(); | 
|  | } | 
|  |  | 
|  | bool IsCanceled(const CancellationFlag* flag, | 
|  | ScopedClosureRunner* cleanup_runner) { | 
|  | return flag->IsSet(); | 
|  | } | 
|  |  | 
|  | void RunAndDeleteFlag(OnceClosure closure, const CancellationFlag* flag) { | 
|  | std::move(closure).Run(); | 
|  | delete flag; | 
|  | } | 
|  |  | 
|  | void RunOrPostToTaskRunner(TaskRunner* task_runner, OnceClosure closure) { | 
|  | if (task_runner->RunsTasksInCurrentSequence()) | 
|  | std::move(closure).Run(); | 
|  | else | 
|  | task_runner->PostTask(FROM_HERE, std::move(closure)); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; | 
|  |  | 
|  | CancelableTaskTracker::CancelableTaskTracker() | 
|  | : next_id_(1),weak_factory_(this) {} | 
|  |  | 
|  | CancelableTaskTracker::~CancelableTaskTracker() { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  |  | 
|  | TryCancelAll(); | 
|  | } | 
|  |  | 
|  | CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( | 
|  | TaskRunner* task_runner, | 
|  | const Location& from_here, | 
|  | OnceClosure task) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  |  | 
|  | return PostTaskAndReply(task_runner, from_here, std::move(task), DoNothing()); | 
|  | } | 
|  |  | 
|  | CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( | 
|  | TaskRunner* task_runner, | 
|  | const Location& from_here, | 
|  | OnceClosure task, | 
|  | OnceClosure reply) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  |  | 
|  | // We need a SequencedTaskRunnerHandle to run |reply|. | 
|  | DCHECK(SequencedTaskRunnerHandle::IsSet()); | 
|  |  | 
|  | // Owned by reply callback below. | 
|  | CancellationFlag* flag = new CancellationFlag(); | 
|  |  | 
|  | TaskId id = next_id_; | 
|  | next_id_++;  // int64_t is big enough that we ignore the potential overflow. | 
|  |  | 
|  | OnceClosure untrack_closure = | 
|  | BindOnce(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id); | 
|  | bool success = task_runner->PostTaskAndReply( | 
|  | from_here, BindOnce(&RunIfNotCanceled, flag, std::move(task)), | 
|  | BindOnce(&RunIfNotCanceledThenUntrack, Owned(flag), std::move(reply), | 
|  | std::move(untrack_closure))); | 
|  |  | 
|  | if (!success) | 
|  | return kBadTaskId; | 
|  |  | 
|  | Track(id, flag); | 
|  | return id; | 
|  | } | 
|  |  | 
|  | CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( | 
|  | IsCanceledCallback* is_canceled_cb) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  | DCHECK(SequencedTaskRunnerHandle::IsSet()); | 
|  |  | 
|  | TaskId id = next_id_; | 
|  | next_id_++;  // int64_t is big enough that we ignore the potential overflow. | 
|  |  | 
|  | // Will be deleted by |untrack_and_delete_flag| after Untrack(). | 
|  | CancellationFlag* flag = new CancellationFlag(); | 
|  |  | 
|  | OnceClosure untrack_and_delete_flag = BindOnce( | 
|  | &RunAndDeleteFlag, | 
|  | BindOnce(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id), | 
|  | flag); | 
|  |  | 
|  | // Will always run |untrack_and_delete_flag| on current sequence. | 
|  | ScopedClosureRunner* untrack_and_delete_flag_runner = | 
|  | new ScopedClosureRunner(BindOnce( | 
|  | &RunOrPostToTaskRunner, RetainedRef(SequencedTaskRunnerHandle::Get()), | 
|  | std::move(untrack_and_delete_flag))); | 
|  |  | 
|  | *is_canceled_cb = | 
|  | Bind(&IsCanceled, flag, Owned(untrack_and_delete_flag_runner)); | 
|  |  | 
|  | Track(id, flag); | 
|  | return id; | 
|  | } | 
|  |  | 
|  | void CancelableTaskTracker::TryCancel(TaskId id) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  |  | 
|  | const auto it = task_flags_.find(id); | 
|  | if (it == task_flags_.end()) { | 
|  | // Two possibilities: | 
|  | // | 
|  | //   1. The task has already been untracked. | 
|  | //   2. The TaskId is bad or unknown. | 
|  | // | 
|  | // Since this function is best-effort, it's OK to ignore these. | 
|  | return; | 
|  | } | 
|  | it->second->Set(); | 
|  | } | 
|  |  | 
|  | void CancelableTaskTracker::TryCancelAll() { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  | for (const auto& it : task_flags_) | 
|  | it.second->Set(); | 
|  | weak_factory_.InvalidateWeakPtrs(); | 
|  | task_flags_.clear(); | 
|  | } | 
|  |  | 
|  | bool CancelableTaskTracker::HasTrackedTasks() const { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  | return !task_flags_.empty(); | 
|  | } | 
|  |  | 
|  | void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  | bool success = task_flags_.insert(std::make_pair(id, flag)).second; | 
|  | DCHECK(success); | 
|  | } | 
|  |  | 
|  | void CancelableTaskTracker::Untrack(TaskId id) { | 
|  | DCHECK(sequence_checker_.CalledOnValidSequence()); | 
|  | size_t num = task_flags_.erase(id); | 
|  | DCHECK_EQ(1u, num); | 
|  | } | 
|  |  | 
|  | }  // namespace base |