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