// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_BASE_PRIORITIZED_TASK_RUNNER_H_ #define NET_BASE_PRIORITIZED_TASK_RUNNER_H_ #include #include #include #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" #include "base/task/post_task_and_reply_with_result_internal.h" #include "base/task/task_traits.h" #include "base/thread_annotations.h" #include "net/base/net_export.h" namespace base { class TaskRunner; } // namespace base namespace net { namespace internal { template void ReturnAsParamAdapter(base::OnceCallback func, ReturnType* result) { *result = std::move(func).Run(); } // Adapts a T* result to a callblack that expects a T. template void ReplyAdapter(base::OnceCallback callback, TaskReturnType* result) { std::move(callback).Run(std::move(*result)); } } // namespace internal // PrioritizedTaskRunner allows for prioritization of posted tasks and their // replies. It provides up to 2^32 priority levels. All tasks posted via the // PrioritizedTaskRunner will run in priority order. All replies from // PostTaskAndReply will also run in priority order. Be careful, as it is // possible to starve a task. class NET_EXPORT_PRIVATE PrioritizedTaskRunner : public base::RefCountedThreadSafe { public: enum class ReplyRunnerType { kStandard, kPrioritized }; explicit PrioritizedTaskRunner(const base::TaskTraits& task_traits); PrioritizedTaskRunner(const PrioritizedTaskRunner&) = delete; PrioritizedTaskRunner& operator=(const PrioritizedTaskRunner&) = delete; // Similar to TaskRunner::PostTaskAndReply, except that the task runs at // |priority|. Priority 0 is the highest priority and will run before other // priority values. Multiple tasks with the same |priority| value are run in // order of posting. The replies are also run in prioritized order on the // calling taskrunner. void PostTaskAndReply(const base::Location& from_here, base::OnceClosure task, base::OnceClosure reply, uint32_t priority); // Similar to TaskRunner::PostTaskAndReplyWithResult, except that the task // runs at |priority|. See PostTaskAndReply for a description of |priority|. template void PostTaskAndReplyWithResult(const base::Location& from_here, base::OnceCallback task, base::OnceCallback reply, uint32_t priority) { TaskReturnType* result = new TaskReturnType(); return PostTaskAndReply( from_here, base::BindOnce(&internal::ReturnAsParamAdapter, std::move(task), result), base::BindOnce(&internal::ReplyAdapter, std::move(reply), base::Owned(result)), priority); } void SetTaskRunnerForTesting(scoped_refptr task_runner) { task_runner_for_testing_ = std::move(task_runner); } private: friend class base::RefCountedThreadSafe; struct Job { Job(const base::Location& from_here, base::OnceClosure task, base::OnceClosure reply, uint32_t priority, uint32_t task_count); Job(); Job(const Job&) = delete; Job& operator=(const Job&) = delete; ~Job(); Job(Job&& other); Job& operator=(Job&& other); base::Location from_here; base::OnceClosure task; base::OnceClosure reply; uint32_t priority = 0; uint32_t task_count = 0; }; struct JobComparer; // A heap of Jobs. Thread-safe. class JobPriorityQueue { public: JobPriorityQueue(); ~JobPriorityQueue(); JobPriorityQueue(const JobPriorityQueue&) = delete; JobPriorityQueue& operator=(const JobPriorityQueue&) = delete; // Add a Job to the heap. void Push(Job job); // Return the current highest-priority job and remove it from the heap. Job Pop(); private: // This cannot be a std::priority_queue because there is no way to extract // a move-only type from a std::priority_queue. std::vector heap_ GUARDED_BY(lock_); base::Lock lock_; }; void RunTaskAndPostReply(); void RunReply(); ~PrioritizedTaskRunner(); // Accessed on both task_runner_ and the reply task runner. JobPriorityQueue task_jobs_; JobPriorityQueue reply_jobs_; const base::TaskTraits task_traits_; scoped_refptr task_runner_for_testing_; // Used to preserve order of jobs of equal priority. This can overflow and // cause periodic priority inversion. This should be infrequent enough to be // of negligible impact. uint32_t task_count_ = 0; }; } // namespace net #endif // NET_BASE_PRIORITIZED_TASK_RUNNER_H_