diff options
Diffstat (limited to 'chromium/base/task/cancelable_task_tracker.cc')
-rw-r--r-- | chromium/base/task/cancelable_task_tracker.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/chromium/base/task/cancelable_task_tracker.cc b/chromium/base/task/cancelable_task_tracker.cc new file mode 100644 index 00000000000..801223e2806 --- /dev/null +++ b/chromium/base/task/cancelable_task_tracker.cc @@ -0,0 +1,187 @@ +// 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 <utility> + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/compiler_specific.h" +#include "base/location.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/synchronization/cancellation_flag.h" +#include "base/task_runner.h" + +using base::Bind; +using base::CancellationFlag; +using base::Closure; +using base::hash_map; +using base::TaskRunner; + +namespace { + +void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) { + if (!flag->IsSet()) + task.Run(); +} + +void RunIfNotCanceledThenUntrack(const CancellationFlag* flag, + const Closure& task, + const Closure& untrack) { + RunIfNotCanceled(flag, task); + untrack.Run(); +} + +bool IsCanceled(const CancellationFlag* flag, + base::ScopedClosureRunner* cleanup_runner) { + return flag->IsSet(); +} + +void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) { + closure.Run(); + delete flag; +} + +void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) { + if (task_runner->RunsTasksOnCurrentThread()) + closure.Run(); + else + task_runner->PostTask(FROM_HERE, closure); +} + +} // namespace + +namespace base { + +// static +const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0; + +CancelableTaskTracker::CancelableTaskTracker() + : weak_factory_(this), next_id_(1) {} + +CancelableTaskTracker::~CancelableTaskTracker() { + DCHECK(thread_checker_.CalledOnValidThread()); + + TryCancelAll(); +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask( + TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const Closure& task) { + DCHECK(thread_checker_.CalledOnValidThread()); + + return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing)); +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( + TaskRunner* task_runner, + const tracked_objects::Location& from_here, + const Closure& task, + const Closure& reply) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // We need a MessageLoop to run reply. + DCHECK(base::MessageLoopProxy::current().get()); + + // Owned by reply callback below. + CancellationFlag* flag = new CancellationFlag(); + + TaskId id = next_id_; + next_id_++; // int64 is big enough that we ignore the potential overflow. + + const Closure& untrack_closure = + Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id); + bool success = + task_runner->PostTaskAndReply(from_here, + Bind(&RunIfNotCanceled, flag, task), + Bind(&RunIfNotCanceledThenUntrack, + base::Owned(flag), + reply, + untrack_closure)); + + if (!success) + return kBadTaskId; + + Track(id, flag); + return id; +} + +CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( + IsCanceledCallback* is_canceled_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(base::MessageLoopProxy::current().get()); + + TaskId id = next_id_; + next_id_++; // int64 is big enough that we ignore the potential overflow. + + // Will be deleted by |untrack_and_delete_flag| after Untrack(). + CancellationFlag* flag = new CancellationFlag(); + + Closure untrack_and_delete_flag = Bind( + &RunAndDeleteFlag, + Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id), + flag); + + // Will always run |untrack_and_delete_flag| on current MessageLoop. + base::ScopedClosureRunner* untrack_and_delete_flag_runner = + new base::ScopedClosureRunner(Bind(&RunOrPostToTaskRunner, + base::MessageLoopProxy::current(), + untrack_and_delete_flag)); + + *is_canceled_cb = + Bind(&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner)); + + Track(id, flag); + return id; +} + +void CancelableTaskTracker::TryCancel(TaskId id) { + DCHECK(thread_checker_.CalledOnValidThread()); + + hash_map<TaskId, CancellationFlag*>::const_iterator 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(thread_checker_.CalledOnValidThread()); + + for (hash_map<TaskId, CancellationFlag*>::const_iterator it = + task_flags_.begin(); + it != task_flags_.end(); + ++it) { + it->second->Set(); + } +} + +bool CancelableTaskTracker::HasTrackedTasks() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return !task_flags_.empty(); +} + +void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) { + DCHECK(thread_checker_.CalledOnValidThread()); + + bool success = task_flags_.insert(std::make_pair(id, flag)).second; + DCHECK(success); +} + +void CancelableTaskTracker::Untrack(TaskId id) { + DCHECK(thread_checker_.CalledOnValidThread()); + size_t num = task_flags_.erase(id); + DCHECK_EQ(1u, num); +} + +} // namespace base |