633 lines
25 KiB
C++
633 lines
25 KiB
C++
// 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 BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_IMPL_H_
|
|
#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_IMPL_H_
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "base/base_export.h"
|
|
#include "base/containers/flat_map.h"
|
|
#include "base/containers/intrusive_heap.h"
|
|
#include "base/dcheck_is_on.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/memory/scoped_refptr.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/observer_list.h"
|
|
#include "base/pending_task.h"
|
|
#include "base/task/common/checked_lock.h"
|
|
#include "base/task/common/operations_controller.h"
|
|
#include "base/task/sequence_manager/associated_thread_id.h"
|
|
#include "base/task/sequence_manager/atomic_flag_set.h"
|
|
#include "base/task/sequence_manager/enqueue_order.h"
|
|
#include "base/task/sequence_manager/fence.h"
|
|
#include "base/task/sequence_manager/lazily_deallocated_deque.h"
|
|
#include "base/task/sequence_manager/sequenced_task_source.h"
|
|
#include "base/task/sequence_manager/task_queue.h"
|
|
#include "base/threading/thread_checker.h"
|
|
#include "base/time/time_override.h"
|
|
#include "base/trace_event/base_tracing_forward.h"
|
|
#include "base/values.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace base {
|
|
class LazyNow;
|
|
namespace sequence_manager::internal {
|
|
|
|
class SequenceManagerImpl;
|
|
class WorkQueue;
|
|
class WorkQueueSets;
|
|
class WakeUpQueue;
|
|
|
|
// TaskQueueImpl has four main queues:
|
|
//
|
|
// Immediate (non-delayed) tasks:
|
|
// |immediate_incoming_queue| - PostTask enqueues tasks here.
|
|
// |immediate_work_queue| - SequenceManager takes immediate tasks here.
|
|
//
|
|
// Delayed tasks
|
|
// |delayed_incoming_queue| - PostDelayedTask enqueues tasks here.
|
|
// |delayed_work_queue| - SequenceManager takes delayed tasks here.
|
|
//
|
|
// The |immediate_incoming_queue| can be accessed from any thread, the other
|
|
// queues are main-thread only. To reduce the overhead of locking,
|
|
// |immediate_work_queue| is swapped with |immediate_incoming_queue| when
|
|
// |immediate_work_queue| becomes empty.
|
|
//
|
|
// Delayed tasks are initially posted to |delayed_incoming_queue| and a wake-up
|
|
// is scheduled with the TimeDomain. When the delay has elapsed, the TimeDomain
|
|
// calls UpdateDelayedWorkQueue and ready delayed tasks are moved into the
|
|
// |delayed_work_queue|. Note the EnqueueOrder (used for ordering) for a delayed
|
|
// task is not set until it's moved into the |delayed_work_queue|.
|
|
//
|
|
// TaskQueueImpl uses the WorkQueueSets and the TaskQueueSelector to implement
|
|
// prioritization. Task selection is done by the TaskQueueSelector and when a
|
|
// queue is selected, it round-robins between the |immediate_work_queue| and
|
|
// |delayed_work_queue|. The reason for this is we want to make sure delayed
|
|
// tasks (normally the most common type) don't starve out immediate work.
|
|
class BASE_EXPORT TaskQueueImpl {
|
|
public:
|
|
// Initializes the state of all the task queue features. Must be invoked
|
|
// after FeatureList initialization and while Chrome is still single-threaded.
|
|
static void InitializeFeatures();
|
|
|
|
// Sets the global cached state of the RemoveCanceledTasksInTaskQueue feature
|
|
// according to its enabled state. Must be invoked after FeatureList
|
|
// initialization.
|
|
static void ApplyRemoveCanceledTasksInTaskQueue();
|
|
|
|
// Resets the global cached state of the RemoveCanceledTasksInTaskQueue
|
|
// feature according to its default state.
|
|
static void ResetRemoveCanceledTasksInTaskQueueForTesting();
|
|
|
|
TaskQueueImpl(SequenceManagerImpl* sequence_manager,
|
|
WakeUpQueue* wake_up_queue,
|
|
const TaskQueue::Spec& spec);
|
|
|
|
TaskQueueImpl(const TaskQueueImpl&) = delete;
|
|
TaskQueueImpl& operator=(const TaskQueueImpl&) = delete;
|
|
~TaskQueueImpl();
|
|
|
|
// Types of queues TaskQueueImpl is maintaining internally.
|
|
enum class WorkQueueType { kImmediate, kDelayed };
|
|
|
|
// Some methods have fast paths when on the main thread.
|
|
enum class CurrentThread { kMainThread, kNotMainThread };
|
|
|
|
// Non-nestable tasks may get deferred but such queue is being maintained on
|
|
// SequenceManager side, so we need to keep information how to requeue it.
|
|
struct DeferredNonNestableTask {
|
|
Task task;
|
|
|
|
// `task_queue` is not a raw_ptr<...> for performance reasons (based on
|
|
// analysis of sampling profiler data and tab_search:top100:2020).
|
|
RAW_PTR_EXCLUSION internal::TaskQueueImpl* task_queue;
|
|
|
|
WorkQueueType work_queue_type;
|
|
};
|
|
|
|
using OnNextWakeUpChangedCallback = RepeatingCallback<void(TimeTicks)>;
|
|
using OnTaskStartedHandler =
|
|
RepeatingCallback<void(const Task&, const TaskQueue::TaskTiming&)>;
|
|
using OnTaskCompletedHandler =
|
|
RepeatingCallback<void(const Task&, TaskQueue::TaskTiming*, LazyNow*)>;
|
|
using OnTaskPostedHandler = RepeatingCallback<void(const Task&)>;
|
|
using TaskExecutionTraceLogger =
|
|
RepeatingCallback<void(perfetto::EventContext&, const Task&)>;
|
|
|
|
// May be called from any thread.
|
|
scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner(
|
|
TaskType task_type) const;
|
|
|
|
// TaskQueue implementation.
|
|
const char* GetName() const;
|
|
QueueName GetProtoName() const;
|
|
bool IsQueueEnabled() const;
|
|
void SetQueueEnabled(bool enabled);
|
|
void SetShouldReportPostedTasksWhenDisabled(bool should_report);
|
|
bool IsEmpty() const;
|
|
size_t GetNumberOfPendingTasks() const;
|
|
bool HasTaskToRunImmediatelyOrReadyDelayedTask() const;
|
|
absl::optional<WakeUp> GetNextDesiredWakeUp();
|
|
void SetQueuePriority(TaskQueue::QueuePriority priority);
|
|
TaskQueue::QueuePriority GetQueuePriority() const;
|
|
void AddTaskObserver(TaskObserver* task_observer);
|
|
void RemoveTaskObserver(TaskObserver* task_observer);
|
|
void InsertFence(TaskQueue::InsertFencePosition position);
|
|
void InsertFenceAt(TimeTicks time);
|
|
void RemoveFence();
|
|
bool HasActiveFence();
|
|
bool BlockedByFence() const;
|
|
void SetThrottler(TaskQueue::Throttler* throttler);
|
|
void ResetThrottler();
|
|
|
|
void UnregisterTaskQueue();
|
|
|
|
// Returns true if a (potentially hypothetical) task with the specified
|
|
// |enqueue_order| could run on the queue. Must be called from the main
|
|
// thread.
|
|
bool CouldTaskRun(EnqueueOrder enqueue_order) const;
|
|
|
|
// Returns true if a task with |enqueue_order| obtained from this queue was
|
|
// ever in the queue while it was disabled, blocked by a fence, or less
|
|
// important than kNormalPriority.
|
|
bool WasBlockedOrLowPriority(EnqueueOrder enqueue_order) const;
|
|
|
|
// Must only be called from the thread this task queue was created on.
|
|
void ReloadEmptyImmediateWorkQueue();
|
|
|
|
Value::Dict AsValue(TimeTicks now, bool force_verbose) const;
|
|
|
|
bool GetQuiescenceMonitored() const { return should_monitor_quiescence_; }
|
|
bool GetShouldNotifyObservers() const { return should_notify_observers_; }
|
|
|
|
void NotifyWillProcessTask(const Task& task,
|
|
bool was_blocked_or_low_priority);
|
|
void NotifyDidProcessTask(const Task& task);
|
|
|
|
// Returns true iff this queue has work that can execute now, i.e. immediate
|
|
// tasks or delayed tasks that have been transferred to the work queue by
|
|
// MoveReadyDelayedTasksToWorkQueue(). Delayed tasks that are still in the
|
|
// incoming queue are not taken into account. Ignores the queue's enabled
|
|
// state and fences.
|
|
bool HasTaskToRunImmediately() const;
|
|
bool HasTaskToRunImmediatelyLocked() const
|
|
EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
|
|
|
|
bool has_pending_high_resolution_tasks() const {
|
|
return main_thread_only()
|
|
.delayed_incoming_queue.has_pending_high_resolution_tasks();
|
|
}
|
|
|
|
WorkQueue* delayed_work_queue() {
|
|
return main_thread_only().delayed_work_queue.get();
|
|
}
|
|
|
|
const WorkQueue* delayed_work_queue() const {
|
|
return main_thread_only().delayed_work_queue.get();
|
|
}
|
|
|
|
WorkQueue* immediate_work_queue() {
|
|
return main_thread_only().immediate_work_queue.get();
|
|
}
|
|
|
|
const WorkQueue* immediate_work_queue() const {
|
|
return main_thread_only().immediate_work_queue.get();
|
|
}
|
|
|
|
TaskExecutionTraceLogger task_execution_trace_logger() const {
|
|
return main_thread_only().task_execution_trace_logger;
|
|
}
|
|
|
|
// Removes all canceled tasks from the front of the delayed incoming queue.
|
|
// After calling this, GetNextDesiredWakeUp() is guaranteed to return a time
|
|
// for a non-canceled task, if one exists. Return true if a canceled task was
|
|
// removed.
|
|
bool RemoveAllCanceledDelayedTasksFromFront(LazyNow* lazy_now);
|
|
|
|
// Enqueues in `delayed_work_queue` all delayed tasks which must run now
|
|
// (cannot be postponed) and possibly some delayed tasks which can run now but
|
|
// could be postponed (due to how tasks are stored, it is not possible to
|
|
// retrieve all such tasks efficiently). Must be called from the main thread.
|
|
void MoveReadyDelayedTasksToWorkQueue(LazyNow* lazy_now,
|
|
EnqueueOrder enqueue_order);
|
|
|
|
void OnWakeUp(LazyNow* lazy_now, EnqueueOrder enqueue_order);
|
|
|
|
const WakeUpQueue* wake_up_queue() const {
|
|
return main_thread_only().wake_up_queue;
|
|
}
|
|
|
|
HeapHandle heap_handle() const { return main_thread_only().heap_handle; }
|
|
|
|
void set_heap_handle(HeapHandle heap_handle) {
|
|
main_thread_only().heap_handle = heap_handle;
|
|
}
|
|
|
|
// Pushes |task| onto the front of the specified work queue. Caution must be
|
|
// taken with this API because you could easily starve out other work.
|
|
// TODO(kraynov): Simplify non-nestable task logic https://crbug.com/845437.
|
|
void RequeueDeferredNonNestableTask(DeferredNonNestableTask task);
|
|
|
|
void PushImmediateIncomingTaskForTest(Task task);
|
|
|
|
// Iterates over |delayed_incoming_queue| removing canceled tasks. In
|
|
// addition MaybeShrinkQueue is called on all internal queues.
|
|
void ReclaimMemory(TimeTicks now);
|
|
|
|
// Allows wrapping TaskQueue to set a handler to subscribe for notifications
|
|
// about started and completed tasks.
|
|
void SetOnTaskStartedHandler(OnTaskStartedHandler handler);
|
|
void OnTaskStarted(const Task& task,
|
|
const TaskQueue::TaskTiming& task_timing);
|
|
|
|
// |task_timing| may be passed in Running state and may not have the end time,
|
|
// so that the handler can run an additional task that is counted as a part of
|
|
// the main task.
|
|
// The handler can call TaskTiming::RecordTaskEnd, which is optional, to
|
|
// finalize the task, and use the resulting timing.
|
|
void SetOnTaskCompletedHandler(OnTaskCompletedHandler handler);
|
|
void OnTaskCompleted(const Task& task,
|
|
TaskQueue::TaskTiming* task_timing,
|
|
LazyNow* lazy_now);
|
|
bool RequiresTaskTiming() const;
|
|
|
|
// Add a callback for adding custom functionality for processing posted task.
|
|
// Callback will be dispatched while holding a scheduler lock. As a result,
|
|
// callback should not call scheduler APIs directly, as this can lead to
|
|
// deadlocks. For example, PostTask should not be called directly and
|
|
// ScopedDeferTaskPosting::PostOrDefer should be used instead. `handler` must
|
|
// not be a null callback.
|
|
[[nodiscard]] std::unique_ptr<TaskQueue::OnTaskPostedCallbackHandle>
|
|
AddOnTaskPostedHandler(OnTaskPostedHandler handler);
|
|
|
|
// Set a callback to fill trace event arguments associated with the task
|
|
// execution.
|
|
void SetTaskExecutionTraceLogger(TaskExecutionTraceLogger logger);
|
|
|
|
WeakPtr<SequenceManagerImpl> GetSequenceManagerWeakPtr();
|
|
|
|
SequenceManagerImpl* sequence_manager() const { return sequence_manager_; }
|
|
|
|
// Returns true if this queue is unregistered or task queue manager is deleted
|
|
// and this queue can be safely deleted on any thread.
|
|
bool IsUnregistered() const;
|
|
|
|
// Updates this queue's next wake up time in the time domain,
|
|
// taking into account the desired run time of queued tasks and
|
|
// policies enforced by the Throttler.
|
|
void UpdateWakeUp(LazyNow* lazy_now);
|
|
|
|
protected:
|
|
// Sets this queue's next wake up time to |wake_up| in the time domain.
|
|
void SetNextWakeUp(LazyNow* lazy_now, absl::optional<WakeUp> wake_up);
|
|
|
|
private:
|
|
friend class WorkQueue;
|
|
friend class WorkQueueTest;
|
|
friend class DelayedTaskHandleDelegate;
|
|
|
|
// A TaskQueueImpl instance can be destroyed or unregistered before all its
|
|
// associated TaskRunner instances are (they are refcounted). Thus we need a
|
|
// way to prevent TaskRunner instances from posting further tasks. This class
|
|
// guards PostTask calls using an OperationsController.
|
|
// This class is ref-counted as both the TaskQueueImpl instance and all
|
|
// associated TaskRunner instances share the same GuardedTaskPoster instance.
|
|
// When TaskQueueImpl shuts down it calls ShutdownAndWaitForZeroOperations(),
|
|
// preventing further PostTask calls being made to the underlying
|
|
// TaskQueueImpl.
|
|
class GuardedTaskPoster : public RefCountedThreadSafe<GuardedTaskPoster> {
|
|
public:
|
|
explicit GuardedTaskPoster(TaskQueueImpl* outer);
|
|
|
|
bool PostTask(PostedTask task);
|
|
DelayedTaskHandle PostCancelableTask(PostedTask task);
|
|
|
|
void StartAcceptingOperations() {
|
|
operations_controller_.StartAcceptingOperations();
|
|
}
|
|
|
|
void ShutdownAndWaitForZeroOperations() {
|
|
operations_controller_.ShutdownAndWaitForZeroOperations();
|
|
// `operations_controller_` won't let any more operations here, and
|
|
// `outer_` might get destroyed before `this` does, so clearing `outer_`
|
|
// avoids a potential dangling pointer.
|
|
outer_ = nullptr;
|
|
}
|
|
|
|
private:
|
|
friend class RefCountedThreadSafe<GuardedTaskPoster>;
|
|
|
|
~GuardedTaskPoster();
|
|
|
|
base::internal::OperationsController operations_controller_;
|
|
// Pointer might be stale, access guarded by |operations_controller_|
|
|
raw_ptr<TaskQueueImpl> outer_;
|
|
};
|
|
|
|
class TaskRunner final : public SingleThreadTaskRunner {
|
|
public:
|
|
explicit TaskRunner(
|
|
scoped_refptr<GuardedTaskPoster> task_poster,
|
|
scoped_refptr<const AssociatedThreadId> associated_thread,
|
|
TaskType task_type);
|
|
|
|
bool PostDelayedTask(const Location& location,
|
|
OnceClosure callback,
|
|
TimeDelta delay) final;
|
|
bool PostDelayedTaskAt(subtle::PostDelayedTaskPassKey,
|
|
const Location& location,
|
|
OnceClosure callback,
|
|
TimeTicks delayed_run_time,
|
|
base::subtle::DelayPolicy delay_policy) final;
|
|
DelayedTaskHandle PostCancelableDelayedTaskAt(
|
|
subtle::PostDelayedTaskPassKey,
|
|
const Location& location,
|
|
OnceClosure callback,
|
|
TimeTicks delayed_run_time,
|
|
base::subtle::DelayPolicy delay_policy) final;
|
|
DelayedTaskHandle PostCancelableDelayedTask(subtle::PostDelayedTaskPassKey,
|
|
const Location& location,
|
|
OnceClosure callback,
|
|
TimeDelta delay) final;
|
|
bool PostNonNestableDelayedTask(const Location& location,
|
|
OnceClosure callback,
|
|
TimeDelta delay) final;
|
|
bool RunsTasksInCurrentSequence() const final;
|
|
|
|
private:
|
|
~TaskRunner() final;
|
|
|
|
const scoped_refptr<GuardedTaskPoster> task_poster_;
|
|
const scoped_refptr<const AssociatedThreadId> associated_thread_;
|
|
const TaskType task_type_;
|
|
};
|
|
|
|
class OnTaskPostedCallbackHandleImpl
|
|
: public TaskQueue::OnTaskPostedCallbackHandle {
|
|
public:
|
|
OnTaskPostedCallbackHandleImpl(
|
|
TaskQueueImpl* task_queue_impl,
|
|
scoped_refptr<const AssociatedThreadId> associated_thread_);
|
|
~OnTaskPostedCallbackHandleImpl() override;
|
|
|
|
// Callback handles can outlive the associated TaskQueueImpl, so the
|
|
// reference needs to be cleared when the queue is unregistered.
|
|
void UnregisterTaskQueue() { task_queue_impl_ = nullptr; }
|
|
|
|
private:
|
|
raw_ptr<TaskQueueImpl> task_queue_impl_;
|
|
const scoped_refptr<const AssociatedThreadId> associated_thread_;
|
|
};
|
|
|
|
// A queue for holding delayed tasks before their delay has expired.
|
|
struct DelayedIncomingQueue {
|
|
public:
|
|
DelayedIncomingQueue();
|
|
DelayedIncomingQueue(const DelayedIncomingQueue&) = delete;
|
|
DelayedIncomingQueue& operator=(const DelayedIncomingQueue&) = delete;
|
|
~DelayedIncomingQueue();
|
|
|
|
void push(Task task);
|
|
void remove(HeapHandle heap_handle);
|
|
Task take_top();
|
|
bool empty() const { return queue_.empty(); }
|
|
size_t size() const { return queue_.size(); }
|
|
const Task& top() const { return queue_.top(); }
|
|
void swap(DelayedIncomingQueue* other);
|
|
|
|
bool has_pending_high_resolution_tasks() const {
|
|
return pending_high_res_tasks_;
|
|
}
|
|
|
|
// TODO(crbug.com/1155905): we pass SequenceManager to be able to record
|
|
// crash keys. Remove this parameter after chasing down this crash.
|
|
void SweepCancelledTasks(SequenceManagerImpl* sequence_manager);
|
|
Value::List AsValue(TimeTicks now) const;
|
|
|
|
private:
|
|
struct Compare {
|
|
bool operator()(const Task& lhs, const Task& rhs) const;
|
|
};
|
|
IntrusiveHeap<Task, Compare> queue_;
|
|
|
|
// Number of pending tasks in the queue that need high resolution timing.
|
|
int pending_high_res_tasks_ = 0;
|
|
};
|
|
|
|
struct MainThreadOnly {
|
|
MainThreadOnly(TaskQueueImpl* task_queue, WakeUpQueue* wake_up_queue);
|
|
~MainThreadOnly();
|
|
|
|
raw_ptr<WakeUpQueue> wake_up_queue;
|
|
|
|
raw_ptr<TaskQueue::Throttler> throttler = nullptr;
|
|
|
|
std::unique_ptr<WorkQueue> delayed_work_queue;
|
|
std::unique_ptr<WorkQueue> immediate_work_queue;
|
|
DelayedIncomingQueue delayed_incoming_queue;
|
|
ObserverList<TaskObserver>::Unchecked task_observers;
|
|
HeapHandle heap_handle;
|
|
bool is_enabled = true;
|
|
absl::optional<Fence> current_fence;
|
|
absl::optional<TimeTicks> delayed_fence;
|
|
// Snapshots the next sequence number when the queue is unblocked, otherwise
|
|
// it contains EnqueueOrder::none(). If the EnqueueOrder of a task just
|
|
// popped from this queue is greater than this, it means that the queue was
|
|
// never disabled or blocked by a fence while the task was queued.
|
|
EnqueueOrder enqueue_order_at_which_we_became_unblocked;
|
|
// If the EnqueueOrder of a task just popped from this queue is greater than
|
|
// this, it means that the queue was never disabled, blocked by a fence or
|
|
// less important than kNormalPriority while the task was queued.
|
|
//
|
|
// Implementation details:
|
|
// 1) When the queue is made less important than kNormalPriority, this is
|
|
// set to EnqueueOrder::max(). The EnqueueOrder of any task will compare
|
|
// less than this.
|
|
// 2) When the queue is made at least as important as kNormalPriority, this
|
|
// snapshots the next sequence number. If the queue is blocked, the value
|
|
// is irrelevant because no task should be popped. If the queue is not
|
|
// blocked, the EnqueueOrder of any already queued task will compare less
|
|
// than this.
|
|
// 3) When the queue is unblocked while at least as important as
|
|
// kNormalPriority, this snapshots the next sequence number. The
|
|
// EnqueueOrder of any already queued task will compare less than this.
|
|
//
|
|
// TODO(crbug.com/1249857): Change this to use `TaskOrder`.
|
|
EnqueueOrder
|
|
enqueue_order_at_which_we_became_unblocked_with_normal_priority;
|
|
OnTaskStartedHandler on_task_started_handler;
|
|
OnTaskCompletedHandler on_task_completed_handler;
|
|
TaskExecutionTraceLogger task_execution_trace_logger;
|
|
// Last reported wake up, used only in UpdateWakeUp to avoid
|
|
// excessive calls.
|
|
absl::optional<WakeUp> scheduled_wake_up;
|
|
// If false, queue will be disabled. Used only for tests.
|
|
bool is_enabled_for_test = true;
|
|
// The time at which the task queue was disabled, if it is currently
|
|
// disabled.
|
|
absl::optional<TimeTicks> disabled_time;
|
|
// Whether or not the task queue should emit tracing events for tasks
|
|
// posted to this queue when it is disabled.
|
|
bool should_report_posted_tasks_when_disabled = false;
|
|
};
|
|
|
|
void PostTask(PostedTask task);
|
|
void RemoveCancelableTask(HeapHandle heap_handle);
|
|
|
|
void PostImmediateTaskImpl(PostedTask task, CurrentThread current_thread);
|
|
void PostDelayedTaskImpl(PostedTask task, CurrentThread current_thread);
|
|
|
|
// Push the task onto the |delayed_incoming_queue|. Lock-free main thread
|
|
// only fast path.
|
|
void PushOntoDelayedIncomingQueueFromMainThread(Task pending_task,
|
|
LazyNow* lazy_now,
|
|
bool notify_task_annotator);
|
|
|
|
// Push the task onto the |delayed_incoming_queue|. Slow path from other
|
|
// threads.
|
|
void PushOntoDelayedIncomingQueue(Task pending_task);
|
|
|
|
void ScheduleDelayedWorkTask(Task pending_task);
|
|
|
|
void MoveReadyImmediateTasksToImmediateWorkQueueLocked()
|
|
EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
|
|
|
|
// Records the delay for some tasks in the main thread and the size of the
|
|
// |delayed_incoming_queue| pseudorandomly in a histogram.
|
|
void RecordQueuingDelayedTaskMetrics(const Task& pending_task,
|
|
LazyNow* lazy_now);
|
|
|
|
// LazilyDeallocatedDeque use TimeTicks to figure out when to resize. We
|
|
// should use real time here always.
|
|
using TaskDeque =
|
|
LazilyDeallocatedDeque<Task, subtle::TimeTicksNowIgnoringOverride>;
|
|
|
|
// Extracts all the tasks from the immediate incoming queue and swaps it with
|
|
// |queue| which must be empty.
|
|
// Can be called from any thread.
|
|
void TakeImmediateIncomingQueueTasks(TaskDeque* queue);
|
|
|
|
void TraceQueueSize() const;
|
|
static Value::List QueueAsValue(const TaskDeque& queue, TimeTicks now);
|
|
static Value::Dict TaskAsValue(const Task& task, TimeTicks now);
|
|
|
|
// Returns a Task representation for `delayed_task`.
|
|
Task MakeDelayedTask(PostedTask delayed_task, LazyNow* lazy_now) const;
|
|
|
|
// Activate a delayed fence if a time has come based on `task`'s delayed run
|
|
// time.
|
|
void ActivateDelayedFenceIfNeeded(const Task& task);
|
|
|
|
// Updates state protected by any_thread_lock_.
|
|
void UpdateCrossThreadQueueStateLocked()
|
|
EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
|
|
|
|
TimeDelta GetTaskDelayAdjustment(CurrentThread current_thread);
|
|
|
|
// Reports the task if it was due to IPC and was posted to a disabled queue.
|
|
// This should be called after WillQueueTask has been called for the task.
|
|
void MaybeReportIpcTaskQueuedFromMainThread(const Task& pending_task);
|
|
bool ShouldReportIpcTaskQueuedFromAnyThreadLocked(
|
|
base::TimeDelta* time_since_disabled)
|
|
EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
|
|
void MaybeReportIpcTaskQueuedFromAnyThreadLocked(const Task& pending_task)
|
|
EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
|
|
void MaybeReportIpcTaskQueuedFromAnyThreadUnlocked(const Task& pending_task);
|
|
void ReportIpcTaskQueued(const Task& pending_task,
|
|
const base::TimeDelta& time_since_disabled);
|
|
|
|
// Invoked when the queue becomes enabled and not blocked by a fence.
|
|
void OnQueueUnblocked();
|
|
|
|
void InsertFence(Fence fence);
|
|
|
|
void RemoveOnTaskPostedHandler(
|
|
OnTaskPostedCallbackHandleImpl* on_task_posted_callback_handle);
|
|
|
|
TaskQueue::QueuePriority DefaultPriority() const;
|
|
|
|
QueueName name_;
|
|
const raw_ptr<SequenceManagerImpl, DanglingUntriaged> sequence_manager_;
|
|
|
|
const scoped_refptr<const AssociatedThreadId> associated_thread_;
|
|
|
|
const scoped_refptr<GuardedTaskPoster> task_poster_;
|
|
|
|
mutable base::internal::CheckedLock any_thread_lock_;
|
|
|
|
struct AnyThread {
|
|
// Mirrored from MainThreadOnly. These are only used for tracing.
|
|
struct TracingOnly {
|
|
TracingOnly();
|
|
~TracingOnly();
|
|
|
|
bool is_enabled = true;
|
|
absl::optional<TimeTicks> disabled_time;
|
|
bool should_report_posted_tasks_when_disabled = false;
|
|
};
|
|
|
|
AnyThread();
|
|
~AnyThread();
|
|
|
|
TaskDeque immediate_incoming_queue;
|
|
|
|
// True if main_thread_only().immediate_work_queue is empty.
|
|
bool immediate_work_queue_empty = true;
|
|
|
|
bool post_immediate_task_should_schedule_work = true;
|
|
|
|
bool unregistered = false;
|
|
|
|
base::flat_map<raw_ptr<OnTaskPostedCallbackHandleImpl>, OnTaskPostedHandler>
|
|
on_task_posted_handlers;
|
|
|
|
#if DCHECK_IS_ON()
|
|
// A cache of |immediate_work_queue->work_queue_set_index()| which is used
|
|
// to index into
|
|
// SequenceManager::Settings::per_priority_cross_thread_task_delay to apply
|
|
// a priority specific delay for debugging purposes.
|
|
size_t queue_set_index = 0;
|
|
#endif
|
|
|
|
TracingOnly tracing_only;
|
|
};
|
|
|
|
AnyThread any_thread_ GUARDED_BY(any_thread_lock_);
|
|
|
|
MainThreadOnly main_thread_only_;
|
|
MainThreadOnly& main_thread_only() {
|
|
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
|
|
return main_thread_only_;
|
|
}
|
|
const MainThreadOnly& main_thread_only() const {
|
|
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
|
|
return main_thread_only_;
|
|
}
|
|
|
|
// Handle to our entry within the SequenceManagers |empty_queues_to_reload_|
|
|
// atomic flag set. Used to signal that this queue needs to be reloaded.
|
|
// If you call SetActive(false) you should do so inside |any_thread_lock_|
|
|
// because there is a danger a cross thread PostTask might reset it before we
|
|
// make |immediate_work_queue| non-empty.
|
|
AtomicFlagSet::AtomicFlag empty_queues_to_reload_handle_;
|
|
|
|
const bool should_monitor_quiescence_;
|
|
const bool should_notify_observers_;
|
|
const bool delayed_fence_allowed_;
|
|
};
|
|
|
|
} // namespace sequence_manager::internal
|
|
} // namespace base
|
|
|
|
#endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_IMPL_H_
|