#ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_H_
#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_H_
#include <stddef.h>
#include <atomic>
#include <optional>
#include <vector>
#include "base/base_export.h"
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "base/pending_task.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/sequenced_task_source.h"
#include "base/task/sequence_manager/task_order.h"
#include "base/task/sequence_manager/work_queue_sets.h"
#include "base/values.h"
namespace base {
namespace sequence_manager {
namespace internal {
class AssociatedThreadId;
class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer {
public:
using SelectTaskOption = SequencedTaskSource::SelectTaskOption;
TaskQueueSelector(scoped_refptr<const AssociatedThreadId> associated_thread,
const SequenceManager::Settings& settings);
TaskQueueSelector(const TaskQueueSelector&) = delete;
TaskQueueSelector& operator=(const TaskQueueSelector&) = delete;
~TaskQueueSelector() override;
void AddQueue(internal::TaskQueueImpl* queue,
TaskQueue::QueuePriority priority);
void RemoveQueue(internal::TaskQueueImpl* queue);
void EnableQueue(internal::TaskQueueImpl* queue);
void DisableQueue(internal::TaskQueueImpl* queue);
void SetQueuePriority(internal::TaskQueueImpl* queue,
TaskQueue::QueuePriority priority);
WorkQueue* SelectWorkQueueToService(
SelectTaskOption option = SelectTaskOption::kDefault);
Value::Dict AsValue() const;
class BASE_EXPORT Observer {
public:
virtual ~Observer() = default;
virtual void OnTaskQueueEnabled(internal::TaskQueueImpl* queue) = 0;
virtual void OnWorkAvailable() = 0;
};
void SetTaskQueueSelectorObserver(Observer* observer);
std::optional<TaskQueue::QueuePriority> GetHighestPendingPriority(
SelectTaskOption option = SelectTaskOption::kDefault) const;
void WorkQueueSetBecameEmpty(size_t set_index) override;
void WorkQueueSetBecameNonEmpty(size_t set_index) override;
void CollectSkippedOverLowerPriorityTasks(
const internal::WorkQueue* selected_work_queue,
std::vector<const Task*>* result) const;
protected:
WorkQueueSets* delayed_work_queue_sets() { return &delayed_work_queue_sets_; }
WorkQueueSets* immediate_work_queue_sets() {
return &immediate_work_queue_sets_;
}
void SetImmediateStarvationCountForTest(int immediate_starvation_count);
class BASE_EXPORT ActivePriorityTracker {
public:
ActivePriorityTracker();
bool HasActivePriority() const { return active_priorities_ != 0; }
bool IsActive(TaskQueue::QueuePriority priority) const {
return active_priorities_ & (size_t{1} << static_cast<size_t>(priority));
}
void SetActive(TaskQueue::QueuePriority priority, bool is_active);
TaskQueue::QueuePriority HighestActivePriority() const;
private:
static_assert(SequenceManager::PrioritySettings::kMaxPriorities <
sizeof(size_t) * 8,
"The number of priorities must be strictly less than the "
"number of bits of |active_priorities_|!");
size_t active_priorities_ = 0;
};
* SetOperation is used to configure ChooseWithPriority() and must have:
*
* static std::optional<WorkQueueAndTaskOrder>
* GetWithPriority(const WorkQueueSets& sets,
* TaskQueue::QueuePriority priority);
*/
struct SetOperationOldest {
static std::optional<WorkQueueAndTaskOrder> GetWithPriority(
const WorkQueueSets& sets,
TaskQueue::QueuePriority priority) {
return sets.GetOldestQueueAndTaskOrderInSet(priority);
}
};
#if DCHECK_IS_ON()
struct SetOperationRandom {
static std::optional<WorkQueueAndTaskOrder> GetWithPriority(
const WorkQueueSets& sets,
TaskQueue::QueuePriority priority) {
return sets.GetRandomQueueAndTaskOrderInSet(priority);
}
};
#endif
template <typename SetOperation>
WorkQueue* ChooseWithPriority(TaskQueue::QueuePriority priority) const {
static const int kMaxDelayedStarvationTasks = 3;
if (immediate_starvation_count_ >= kMaxDelayedStarvationTasks) {
WorkQueue* queue =
ChooseImmediateOnlyWithPriority<SetOperation>(priority);
if (queue) {
return queue;
}
return ChooseDelayedOnlyWithPriority<SetOperation>(priority);
}
return ChooseImmediateOrDelayedTaskWithPriority<SetOperation>(priority);
}
template <typename SetOperation>
WorkQueue* ChooseImmediateOnlyWithPriority(
TaskQueue::QueuePriority priority) const {
if (auto queue_and_order = SetOperation::GetWithPriority(
immediate_work_queue_sets_, priority)) {
return queue_and_order->queue;
}
return nullptr;
}
template <typename SetOperation>
WorkQueue* ChooseDelayedOnlyWithPriority(
TaskQueue::QueuePriority priority) const {
if (auto queue_and_order =
SetOperation::GetWithPriority(delayed_work_queue_sets_, priority)) {
return queue_and_order->queue;
}
return nullptr;
}
private:
size_t priority_count() const { return non_empty_set_counts_.size(); }
void ChangeSetIndex(internal::TaskQueueImpl* queue,
TaskQueue::QueuePriority priority);
void AddQueueImpl(internal::TaskQueueImpl* queue,
TaskQueue::QueuePriority priority);
void RemoveQueueImpl(internal::TaskQueueImpl* queue);
#if DCHECK_IS_ON() || !defined(NDEBUG)
bool CheckContainsQueueForTest(const internal::TaskQueueImpl* queue) const;
#endif
template <typename SetOperation>
WorkQueue* ChooseImmediateOrDelayedTaskWithPriority(
TaskQueue::QueuePriority priority) const {
if (auto immediate_queue_and_order = SetOperation::GetWithPriority(
immediate_work_queue_sets_, priority)) {
if (auto delayed_queue_and_order = SetOperation::GetWithPriority(
delayed_work_queue_sets_, priority)) {
return immediate_queue_and_order->order < delayed_queue_and_order->order
? immediate_queue_and_order->queue
: delayed_queue_and_order->queue;
}
return immediate_queue_and_order->queue;
}
return ChooseDelayedOnlyWithPriority<SetOperation>(priority);
}
bool HasTasksWithPriority(TaskQueue::QueuePriority priority) const;
const scoped_refptr<const AssociatedThreadId> associated_thread_;
#if DCHECK_IS_ON()
const bool random_task_selection_ = false;
#endif
std::vector<int> non_empty_set_counts_;
static constexpr const int kMaxNonEmptySetCount = 2;
static std::atomic_int g_max_delayed_starvation_tasks;
ActivePriorityTracker active_priority_tracker_;
WorkQueueSets delayed_work_queue_sets_;
WorkQueueSets immediate_work_queue_sets_;
int immediate_starvation_count_ = 0;
raw_ptr<Observer> task_queue_selector_observer_ = nullptr;
};
}
}
}
#endif