// Copyright 2014 The Chromium Authors // 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 #include #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/metrics/histogram_macros.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_runner.h" namespace base { namespace { void RunOrPostToTaskRunner(scoped_refptr 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() { weak_this_ = weak_factory_.GetWeakPtr(); } CancelableTaskTracker::~CancelableTaskTracker() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); TryCancelAll(); } CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( TaskRunner* task_runner, const Location& from_here, OnceClosure task) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(weak_this_); 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_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(weak_this_); // We need a SequencedTaskRunner::CurrentDefaultHandle to run |reply|. DCHECK(SequencedTaskRunner::HasCurrentDefault()); auto flag = MakeRefCounted(); TaskId id = next_id_; next_id_++; // int64_t is big enough that we ignore the potential overflow. // Unretained(this) is safe because |flag| will have been set to the // "canceled" state after |this| is deleted. OnceClosure untrack_closure = BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id); bool success = task_runner->PostTaskAndReply( from_here, BindOnce(&RunIfNotCanceled, flag, std::move(task)), BindOnce(&RunThenUntrackIfNotCanceled, flag, std::move(reply), std::move(untrack_closure))); if (!success) return kBadTaskId; Track(id, std::move(flag)); return id; } CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( IsCanceledCallback* is_canceled_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(SequencedTaskRunner::HasCurrentDefault()); TaskId id = next_id_; next_id_++; // int64_t is big enough that we ignore the potential overflow. auto flag = MakeRefCounted(); // Unretained(this) is safe because |flag| will have been set to the // "canceled" state after |this| is deleted. OnceClosure untrack_closure = BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id); // Will always run |untrack_closure| on current sequence. ScopedClosureRunner untrack_runner( BindOnce(&RunOrPostToTaskRunner, SequencedTaskRunner::GetCurrentDefault(), BindOnce(&RunIfNotCanceled, flag, std::move(untrack_closure)))); *is_canceled_cb = BindRepeating(&IsCanceled, flag, std::move(untrack_runner)); Track(id, std::move(flag)); return id; } void CancelableTaskTracker::TryCancel(TaskId id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 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->data.Set(); // Remove |id| from |task_flags_| immediately, since we have no further // use for tracking it. This allows the reply closures (see // PostTaskAndReply()) for cancelled tasks to be skipped, since they have // no clean-up to perform. task_flags_.erase(it); } void CancelableTaskTracker::TryCancelAll() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); for (const auto& it : task_flags_) it.second->data.Set(); task_flags_.clear(); } bool CancelableTaskTracker::HasTrackedTasks() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return !task_flags_.empty(); } // static void CancelableTaskTracker::RunIfNotCanceled( const scoped_refptr& flag, OnceClosure task) { if (!flag->data.IsSet()) { std::move(task).Run(); } } // static void CancelableTaskTracker::RunThenUntrackIfNotCanceled( const scoped_refptr& flag, OnceClosure task, OnceClosure untrack) { RunIfNotCanceled(flag, std::move(task)); RunIfNotCanceled(flag, std::move(untrack)); } // static bool CancelableTaskTracker::IsCanceled( const scoped_refptr& flag, const ScopedClosureRunner& cleanup_runner) { return flag->data.IsSet(); } void CancelableTaskTracker::Track(TaskId id, scoped_refptr flag) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(weak_this_); bool success = task_flags_.insert(std::make_pair(id, std::move(flag))).second; DCHECK(success); } void CancelableTaskTracker::Untrack(TaskId id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(weak_this_); size_t num = task_flags_.erase(id); DCHECK_EQ(1u, num); } } // namespace base