blob: 7aa393f6e1d2737e2b8b2340f9adebca8917c45b [file] [log] [blame]
// Copyright 2015 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/time_domain.h"
#include "net/base/request_priority.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_group_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
namespace base {
namespace sequence_manager {
class SequenceManager;
}
} // namespace base
namespace blink {
namespace scheduler {
using TaskQueue = base::sequence_manager::TaskQueue;
namespace main_thread_scheduler_impl_unittest {
class MainThreadSchedulerImplTest;
}
namespace agent_interference_recorder_test {
class AgentInterferenceRecorderTest;
}
namespace task_queue_throttler_unittest {
class TaskQueueThrottlerTest;
}
class FrameSchedulerImpl;
class MainThreadSchedulerImpl;
// TODO(kdillon): Remove ref-counting of MainThreadTaskQueues as it's no longer
// needed.
class PLATFORM_EXPORT MainThreadTaskQueue
: public base::RefCountedThreadSafe<MainThreadTaskQueue> {
public:
enum class QueueType {
// Keep MainThreadTaskQueue::NameForQueueType in sync.
// This enum is used for a histogram and it should not be re-numbered.
// TODO(altimin): Clean up obsolete names and use a new histogram when
// the situation settles.
kControl = 0,
kDefault = 1,
// 2 was used for default loading task runner but this was deprecated.
// 3 was used for default timer task runner but this was deprecated.
// 4: kUnthrottled, obsolete.
kFrameLoading = 5,
// 6 : kFrameThrottleable, replaced with FRAME_THROTTLEABLE.
// 7 : kFramePausable, replaced with kFramePausable
kCompositor = 8,
kIdle = 9,
kTest = 10,
kFrameLoadingControl = 11,
kFrameThrottleable = 12,
kFrameDeferrable = 13,
kFramePausable = 14,
kFrameUnpausable = 15,
kV8 = 16,
// 17 : kIPC, obsolete
kInput = 18,
// Detached is used in histograms for tasks which are run after frame
// is detached and task queue is gracefully shutdown.
// TODO(altimin): Move to the top when histogram is renumbered.
kDetached = 19,
// 20 : kCleanup, obsolete.
// 21 : kWebSchedulingUserInteraction, obsolete.
// 22 : kWebSchedulingBestEffort, obsolete.
kWebScheduling = 24,
kNonWaking = 25,
kIPCTrackingForCachedPages = 26,
// Used to group multiple types when calculating Expected Queueing Time.
kOther = 23,
kCount = 27
};
// The ThrottleHandle controls throttling and unthrottling the queue. When
// a caller requests a queue to be throttled, this handle is returned and
// the queue will remain throttled as long as the handle is alive.
class ThrottleHandle {
public:
ThrottleHandle(base::WeakPtr<TaskQueue> task_queue,
base::WeakPtr<TaskQueueThrottler> throttler)
: task_queue_(std::move(task_queue)), throttler_(std::move(throttler)) {
if (task_queue_ && throttler_)
throttler_->IncreaseThrottleRefCount(task_queue_.get());
}
~ThrottleHandle() {
if (task_queue_ && throttler_)
throttler_->DecreaseThrottleRefCount(task_queue_.get());
}
// Move-only.
ThrottleHandle(ThrottleHandle&& other)
: task_queue_(std::move(other.task_queue_)),
throttler_(std::move(other.throttler_)) {
other.task_queue_ = nullptr;
other.throttler_ = nullptr;
}
ThrottleHandle& operator=(ThrottleHandle&&);
private:
base::WeakPtr<TaskQueue> task_queue_;
base::WeakPtr<TaskQueueThrottler> throttler_;
};
// Returns name of the given queue type. Returned string has application
// lifetime.
static const char* NameForQueueType(QueueType queue_type);
// Returns true if task queues of the given queue type can be created on a
// per-frame basis, and false if they are only created on a shared basis for
// the entire main thread.
static bool IsPerFrameTaskQueue(QueueType);
using QueueTraitsKeyType = int;
// QueueTraits represent the deferrable, throttleable, pausable, and freezable
// properties of a MainThreadTaskQueue. For non-loading task queues, there
// will be at most one task queue with a specific set of QueueTraits, and the
// the QueueTraits determine which queues should be used to run which task
// types.
struct QueueTraits {
QueueTraits()
: can_be_deferred(false),
can_be_throttled(false),
can_be_intensively_throttled(false),
can_be_paused(false),
can_be_frozen(false),
can_run_in_background(true),
can_run_when_virtual_time_paused(true),
can_be_paused_for_android_webview(false) {}
// Separate enum class for handling prioritisation decisions in task queues.
enum class PrioritisationType {
kInternalScriptContinuation = 0,
kBestEffort = 1,
kRegular = 2,
kLoading = 3,
kLoadingControl = 4,
kFindInPage = 5,
kExperimentalDatabase = 6,
kJavaScriptTimer = 7,
kHighPriorityLocalFrame = 8,
kCompositor = 9, // Main-thread only.
kInput = 10,
kCount = 11
};
// kPrioritisationTypeWidthBits is the number of bits required
// for PrioritisationType::kCount - 1, which is the number of bits needed
// to represent |prioritisation_type| in QueueTraitKeyType.
// We need to update it whenever there is a change in
// PrioritisationType::kCount.
// TODO(sreejakshetty) make the number of bits calculation automated.
static constexpr int kPrioritisationTypeWidthBits = 4;
static_assert(static_cast<int>(PrioritisationType::kCount) <=
(1 << kPrioritisationTypeWidthBits),
"Wrong Instanstiation for kPrioritisationTypeWidthBits");
QueueTraits(const QueueTraits&) = default;
QueueTraits SetCanBeDeferred(bool value) {
can_be_deferred = value;
return *this;
}
QueueTraits SetCanBeThrottled(bool value) {
can_be_throttled = value;
return *this;
}
QueueTraits SetCanBeIntensivelyThrottled(bool value) {
can_be_intensively_throttled = value;
return *this;
}
QueueTraits SetCanBePaused(bool value) {
can_be_paused = value;
return *this;
}
QueueTraits SetCanBeFrozen(bool value) {
can_be_frozen = value;
return *this;
}
QueueTraits SetCanRunInBackground(bool value) {
can_run_in_background = value;
return *this;
}
QueueTraits SetCanRunWhenVirtualTimePaused(bool value) {
can_run_when_virtual_time_paused = value;
return *this;
}
QueueTraits SetPrioritisationType(PrioritisationType type) {
prioritisation_type = type;
return *this;
}
QueueTraits SetCanBePausedForAndroidWebview(bool value) {
can_be_paused_for_android_webview = value;
return *this;
}
bool operator==(const QueueTraits& other) const {
return can_be_deferred == other.can_be_deferred &&
can_be_throttled == other.can_be_throttled &&
can_be_intensively_throttled ==
other.can_be_intensively_throttled &&
can_be_paused == other.can_be_paused &&
can_be_frozen == other.can_be_frozen &&
can_run_in_background == other.can_run_in_background &&
can_run_when_virtual_time_paused ==
other.can_run_when_virtual_time_paused &&
prioritisation_type == other.prioritisation_type &&
can_be_paused_for_android_webview ==
other.can_be_paused_for_android_webview;
}
// Return a key suitable for WTF::HashMap.
QueueTraitsKeyType Key() const {
// offset for shifting bits to compute |key|.
// |key| starts at 1 since 0 and -1 are used for empty/deleted values.
int offset = 0;
int key = 1 << (offset++);
key |= can_be_deferred << (offset++);
key |= can_be_throttled << (offset++);
key |= can_be_intensively_throttled << (offset++);
key |= can_be_paused << (offset++);
key |= can_be_frozen << (offset++);
key |= can_run_in_background << (offset++);
key |= can_run_when_virtual_time_paused << (offset++);
key |= can_be_paused_for_android_webview << (offset++);
key |= static_cast<int>(prioritisation_type) << offset;
offset += kPrioritisationTypeWidthBits;
return key;
}
void WriteIntoTracedValue(perfetto::TracedValue context) const;
bool can_be_deferred : 1;
bool can_be_throttled : 1;
bool can_be_intensively_throttled : 1;
bool can_be_paused : 1;
bool can_be_frozen : 1;
bool can_run_in_background : 1;
bool can_run_when_virtual_time_paused : 1;
bool can_be_paused_for_android_webview : 1;
PrioritisationType prioritisation_type = PrioritisationType::kRegular;
};
struct QueueCreationParams {
explicit QueueCreationParams(QueueType queue_type)
: queue_type(queue_type),
spec(NameForQueueType(queue_type)),
agent_group_scheduler(nullptr),
frame_scheduler(nullptr),
freeze_when_keep_active(false) {}
QueueCreationParams SetFreezeWhenKeepActive(bool value) {
freeze_when_keep_active = value;
return *this;
}
QueueCreationParams SetWebSchedulingPriority(
base::Optional<WebSchedulingPriority> priority) {
web_scheduling_priority = priority;
return *this;
}
QueueCreationParams SetAgentGroupScheduler(
AgentGroupSchedulerImpl* scheduler) {
agent_group_scheduler = scheduler;
return *this;
}
QueueCreationParams SetFrameScheduler(FrameSchedulerImpl* scheduler) {
frame_scheduler = scheduler;
return *this;
}
// Forwarded calls to |queue_traits|
QueueCreationParams SetCanBeDeferred(bool value) {
queue_traits = queue_traits.SetCanBeDeferred(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetCanBeThrottled(bool value) {
queue_traits = queue_traits.SetCanBeThrottled(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetCanBePaused(bool value) {
queue_traits = queue_traits.SetCanBePaused(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetCanBeFrozen(bool value) {
queue_traits = queue_traits.SetCanBeFrozen(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetCanRunInBackground(bool value) {
queue_traits = queue_traits.SetCanRunInBackground(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetCanRunWhenVirtualTimePaused(bool value) {
queue_traits = queue_traits.SetCanRunWhenVirtualTimePaused(value);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetPrioritisationType(
QueueTraits::PrioritisationType type) {
queue_traits = queue_traits.SetPrioritisationType(type);
ApplyQueueTraitsToSpec();
return *this;
}
QueueCreationParams SetQueueTraits(QueueTraits value) {
queue_traits = value;
ApplyQueueTraitsToSpec();
return *this;
}
// Forwarded calls to |spec|.
QueueCreationParams SetShouldMonitorQuiescence(bool should_monitor) {
spec = spec.SetShouldMonitorQuiescence(should_monitor);
return *this;
}
QueueCreationParams SetShouldNotifyObservers(bool run_observers) {
spec = spec.SetShouldNotifyObservers(run_observers);
return *this;
}
QueueCreationParams SetTimeDomain(
base::sequence_manager::TimeDomain* domain) {
spec = spec.SetTimeDomain(domain);
return *this;
}
QueueType queue_type;
TaskQueue::Spec spec;
AgentGroupSchedulerImpl* agent_group_scheduler;
FrameSchedulerImpl* frame_scheduler;
QueueTraits queue_traits;
bool freeze_when_keep_active;
base::Optional<WebSchedulingPriority> web_scheduling_priority;
private:
void ApplyQueueTraitsToSpec() {
spec = spec.SetDelayedFencesAllowed(queue_traits.can_be_throttled);
}
};
QueueType queue_type() const { return queue_type_; }
bool CanBeDeferred() const { return queue_traits_.can_be_deferred; }
bool CanBeThrottled() const { return queue_traits_.can_be_throttled; }
bool CanBeIntensivelyThrottled() const {
return queue_traits_.can_be_intensively_throttled;
}
bool CanBePaused() const { return queue_traits_.can_be_paused; }
// Used for WebView's pauseTimers API. This API expects layout, parsing, and
// Javascript timers to be paused. Though this suggests we should pause
// loading (where parsing happens) as well, there are some expectations of JS
// still being able to run during pause. Because of this we only pause timers
// as well as any other pausable frame task queue.
// https://developer.android.com/reference/android/webkit/WebView#pauseTimers()
bool CanBePausedForAndroidWebview() const {
return queue_traits_.can_be_paused_for_android_webview;
}
bool CanBeFrozen() const { return queue_traits_.can_be_frozen; }
bool CanRunInBackground() const {
return queue_traits_.can_run_in_background;
}
bool CanRunWhenVirtualTimePaused() const {
return queue_traits_.can_run_when_virtual_time_paused;
}
bool FreezeWhenKeepActive() const { return freeze_when_keep_active_; }
QueueTraits GetQueueTraits() const { return queue_traits_; }
QueueTraits::PrioritisationType GetPrioritisationType() const {
return queue_traits_.prioritisation_type;
}
void OnTaskReady(const void* frame_scheduler,
const base::sequence_manager::Task& task,
base::sequence_manager::LazyNow* lazy_now);
void OnTaskStarted(const base::sequence_manager::Task& task,
const TaskQueue::TaskTiming& task_timing);
void OnTaskCompleted(const base::sequence_manager::Task& task,
TaskQueue::TaskTiming* task_timing,
base::sequence_manager::LazyNow* lazy_now);
void SetOnIPCTaskPosted(
base::RepeatingCallback<void(const base::sequence_manager::Task&)>
on_ipc_task_posted_callback);
void DetachOnIPCTaskPostedWhileInBackForwardCache();
void DetachFromMainThreadScheduler();
void ShutdownTaskQueue();
WebAgentGroupScheduler* GetAgentGroupScheduler();
FrameSchedulerImpl* GetFrameScheduler() const;
scoped_refptr<base::SingleThreadTaskRunner> CreateTaskRunner(
TaskType task_type) {
return task_queue_->CreateTaskRunner(static_cast<int>(task_type));
}
void SetNetRequestPriority(net::RequestPriority net_request_priority);
base::Optional<net::RequestPriority> net_request_priority() const;
void SetWebSchedulingPriority(WebSchedulingPriority priority);
base::Optional<WebSchedulingPriority> web_scheduling_priority() const;
// TODO(kdillon): Improve MTTQ API surface so that we no longer
// need to expose the raw pointer to the queue.
TaskQueue* GetTaskQueue() { return task_queue_.get(); }
// This method returns the default task runner with task type kTaskTypeNone
// and is mostly used for tests. For most use cases, you'll want a more
// specific task runner and should use the 'CreateTaskRunner' method and pass
// the desired task type.
const scoped_refptr<base::SingleThreadTaskRunner>&
GetTaskRunnerWithDefaultTaskType() {
return task_queue_->task_runner();
}
bool IsThrottled() const;
// Throttles the task queue as long as the handle is kept alive.
MainThreadTaskQueue::ThrottleHandle Throttle();
// Called when a task finished running to update cpu-based throttling.
void OnTaskRunTimeReported(TaskQueue::TaskTiming* task_timing);
// Methods for setting and resetting budget pools for this task queue.
// Note that a task queue can be in multiple budget pools so a pool must
// be specified when resetting.
void AddToBudgetPool(base::TimeTicks now, BudgetPool* pool);
void RemoveFromBudgetPool(base::TimeTicks now, BudgetPool* pool);
// This method is only used for tests. If this queue is throttled it will
// notify the throttler that this queue should wake immediately.
void SetImmediateWakeUpForTest();
base::WeakPtr<MainThreadTaskQueue> AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void WriteIntoTracedValue(perfetto::TracedValue context) const;
protected:
void SetFrameSchedulerForTest(FrameSchedulerImpl* frame_scheduler);
// Returns the underlying task queue. Only to be used for tests that need to
// test functionality of the task queue specifically without the wrapping
// MainThreadTaskQueue (ex TaskQueueThrottlerTest).
TaskQueue* GetTaskQueueForTest() { return task_queue_.get(); }
// TODO(kdillon): Remove references to TaskQueueImpl once TaskQueueImpl
// inherits from TaskQueue.
MainThreadTaskQueue(
std::unique_ptr<base::sequence_manager::internal::TaskQueueImpl> impl,
const TaskQueue::Spec& spec,
const QueueCreationParams& params,
MainThreadSchedulerImpl* main_thread_scheduler);
~MainThreadTaskQueue();
private:
friend class base::RefCountedThreadSafe<MainThreadTaskQueue>;
friend class base::sequence_manager::SequenceManager;
friend class blink::scheduler::main_thread_scheduler_impl_unittest::
MainThreadSchedulerImplTest;
friend class agent_interference_recorder_test::AgentInterferenceRecorderTest;
friend class blink::scheduler::task_queue_throttler_unittest::
TaskQueueThrottlerTest;
// Clear references to main thread scheduler and frame scheduler and dispatch
// appropriate notifications. This is the common part of ShutdownTaskQueue and
// DetachFromMainThreadScheduler.
void ClearReferencesToSchedulers();
scoped_refptr<TaskQueue> task_queue_;
const QueueType queue_type_;
const QueueTraits queue_traits_;
const bool freeze_when_keep_active_;
// Warning: net_request_priority is not the same as the priority of the queue.
// It is the priority (at the loading stack level) of the resource associated
// to the queue, if one exists.
//
// Used to track UMA metrics for resource loading tasks split by net priority.
base::Optional<net::RequestPriority> net_request_priority_;
// |web_scheduling_priority_| is the priority of the task queue within the web
// scheduling API. This priority is used in conjunction with the frame
// scheduling policy to determine the task queue priority.
base::Optional<WebSchedulingPriority> web_scheduling_priority_;
// Needed to notify renderer scheduler about completed tasks.
MainThreadSchedulerImpl* main_thread_scheduler_; // NOT OWNED
AgentGroupSchedulerImpl* agent_group_scheduler_{nullptr}; // NOT OWNED
// Set in the constructor. Cleared in ClearReferencesToSchedulers(). Can never
// be set to a different value afterwards (except in tests).
FrameSchedulerImpl* frame_scheduler_; // NOT OWNED
base::WeakPtrFactory<MainThreadTaskQueue> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MainThreadTaskQueue);
};
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_