浏览器原理 37 # 任务调度:有了setTimeOut,为什么还要使用 requestAnimationFrame?

简介: 浏览器原理 37 # 任务调度:有了setTimeOut,为什么还要使用 requestAnimationFrame?

说明

浏览器工作原理与实践专栏学习笔记



什么是 requestAnimationFrame

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame

20210623210056593.png


requestAnimationFrame

   实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout 来实现,css3 可以使用 transition 和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。


   屏幕刷新频率:屏幕每秒出现图像的次数。普通笔记本为60Hz

   动画原理:计算机每16.7ms刷新一次,由于人眼的视觉停留,所以看起来是流畅的移动。


   优雅降级:由于兼容性问题,需要降级对接口进行封装,优先使用高级特性,再根据浏览器不同情况进行回退,直到只能使用settimeout。


setTimeout:通过设定间隔时间来不断改变图像位置,达到动画效果。但是容易出现卡顿、抖动的现象;


原因是:


   settimeout 任务被放入异步队列,只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚;


   settimeout 的固定时间间隔不一定与屏幕刷新时间相同,会引起丢帧。


requestAnimationFrame:由系统决定回调函数的执行时机。60Hz的刷新频率,那么每次刷新的间隔中会执行一次回调函数,不会引起丢帧,不会卡顿。


优点:


   CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。


   函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。



单消息队列的队头阻塞问题

任务和消息队列:

20210623213240782.png


队头阻塞问题:

20210623213810235.png



在单消息队列架构下,存在着低优先级任务会阻塞高优先级任务的情况,这个问题称为消息队列的队头阻塞问题。



Chromium 是如何解决队头阻塞问题的?


1. 第一次迭代:引入一个高优先级队列


应该视为高优先级的任务:

  • 通过鼠标触发的点击任务、滚动页面任务;
  • 通过手势触发的页面缩放任务;
  • 通过 CSS、JavaScript 等操作触发的动画特效等任务。


引入高优先级的消息队列:

20210623224505868.png


在渲染进程中引入一个任务调度器,负责从多个消息队列中选出合适的任务,通常实现的逻辑,先按照顺序从高优先级队列中取出任务,如果高优先级的队列为空,那么再按照顺序从低优级队列中取出任务。


增加多个不同优先级的消息队列:


20210623233145527.png


缺点:任务的相对执行顺序会被打乱。

怎么解决这个问题?

让一些相同类型的任务保持其相对执行顺序。



2. 第二次迭代:根据消息类型来实现消息队列

不同类型的任务创建不同优先级的消息队列:


  • 可以创建输入事件的消息队列,用来存放输入事件。
  • 可以创建合成任务的消息队列,用来存放合成事件。
  • 可以创建默认消息队列,用来保存如资源加载的事件和定时器回调等事件。
  • 还可以创建一个空闲消息队列,用来存放 V8 的垃圾自动垃圾回收这一类实时性不高的事件。


根据消息类型实现不同优先级的消息队列:

20210624110824531.png


缺点:消息队列的优先级都是固定的,任务调度器会按照这种固定好的静态的优先级来分别调度任务。使用静态优先级策略,网页的加载速度会被拖慢。



3. 第三次迭代:动态调度策略

Chromium 当前所采用的任务调度策略:动态调度策略

20210624113522720.png


页面加载阶段

这个阶段,用户的最高诉求是在尽可能短的时间内看到页面,应将页面解析,JavaScript 脚本执行等任务调整为优先级最高的队列,而交互和合成并不是这个阶段的核心诉求。


交互阶段

前缓冲区


   在显卡中有一块叫着前缓冲区的地方,这里存放着显示器要显示的图像,显示器会按照一定的频率来读取这块前缓冲区,并将前缓冲区中的图像显示在显示器上,不同的显示器读取的频率是不同的,通常情况下是 60HZ,也就是说显示器会每间隔 1/60 秒就读取一次前缓冲区。


如果浏览器要更新显示的图片,那么浏览器会将新生成的图片提交到显卡的后缓冲区中,提交完成之后,GPU 会将后缓冲区和前缓冲区互换位置,所以显示器从前缓冲区读取图片,和浏览器生成新的图像到后缓冲区的过程是不同步的。


不同步会造成众多问题:掉帧、卡顿、不连贯等。


   如果渲染进程生成的帧速比屏幕的刷新率慢,那么屏幕会在两帧中显示同一个画面,当这种断断续续的情况持续发生时,用户将会很明显地察觉到动画卡住了。


   如果渲染进程生成的帧速率实际上比屏幕刷新率快,那么也会出现一些视觉上的问题,比如当帧速率在 100fps 而刷新率只有 60Hz 的时候,GPU 所渲染的图像并非全都被显示出来,这就会造成丢帧现象。

   屏幕的刷新频率和 GPU 更新图片的频率一样,由于它们是两个不同的系统,所以屏幕生成帧的周期和 VSync 的周期也是很难同步起来的。


VSync 时钟周期和渲染引擎生成图片不同步问题:


20210624142614754.png



解决方案:需要将显示器的时钟同步周期和浏览器生成页面的周期绑定起来。


当显示器将一帧画面绘制完成后,并在准备读取下一帧之前,显示器会发出一个垂直同步信号(vertical synchronization)给 GPU,简称 VSync。当 GPU 接收到 VSync 信号后,会将 VSync 信号同步给浏览器进程,浏览器进程再将其同步到对应的渲染进程,渲染进程接收到 VSync 信号之后,就可以准备绘制新的一帧。


绑定 VSync 时钟同步周期和浏览器生成页面周期:


2021062415193349.png


当在执行用户交互的任务时,将合成任务的优先级调整到最高。处理完成 DOM,计算好布局和绘制,就需要将信息提交给合成线程来合成最终图片了,然后合成线程进入工作状态。把下个合成任务的优先级调整为最低,并将页面解析、定时器等任务优先级提升。


在合成完成之后,合成线程会提交给渲染主线程提交完成合成的消息,如果当前合成操作执行的非常快,比如从用户发出消息到完成合成操作只花了 8 毫秒,因为 VSync 同步周期是 16.66(1/60) 毫秒,那么这个 VSync 时钟周期内就不需要再次生成新的页面了。那么从合成结束到下个 VSync 周期内,就进入了一个空闲时间阶段,那么就可以在这段空闲时间内执行一些不那么紧急的任务,比如 V8 的垃圾回收,或者通过 window.requestIdleCallback() 设置的回调任务等,都会在这段空闲时间内执行。


实现过程可以参考这篇文章:Improved vsync scheduling for Chrome on Android


拓展:HSync、VSync

这里插入一点小知识:具体可以参考:iOS 保持界面流畅的技巧

屏幕显示图像的原理


20210624144447790.png


首先从过去的 CRT 显示器原理说起。


CRT 的电子枪按照上面方式,从上到下一行行扫描,扫描完成后显示器就呈现一帧画面,随后电子枪回到初始位置继续下一次扫描。


为了把显示器的显示过程和系统的视频控制器进行同步,显示器(或者其他硬件)会用硬件时钟产生一系列的定时信号。当电子枪换到新的一行,准备进行扫描时,显示器会发出一个水平同步信号(horizonal synchronization),简称 HSync;而当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(vertical synchronization),简称 VSync。


显示器通常以固定频率进行刷新,这个刷新率就是 VSync 信号产生的频率。尽管现在的设备大都是液晶显示屏了,但原理仍然没有变。



20210624145251330.png

通常来说,计算机系统中 CPU、GPU、显示器是以上面这种方式协同工作的。


CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。


在最简单的情况下,帧缓冲区只有一个,这时帧缓冲区的读取和刷新都都会有比较大的效率问题。为了解决效率问题,显示系统通常会引入两个缓冲区,即双缓冲机制。在这种情况下,GPU 会预先渲染好一帧放入一个缓冲区内,让视频控制器读取,当下一帧渲染好后,GPU 会直接把视频控制器的指针指向第二个缓冲器。如此一来效率会有很大的提升。


双缓冲虽然能解决效率问题,但会引入一个新的问题。当视频控制器还未读取完成时,即屏幕内容刚显示一半时,GPU 将新的一帧内容提交到帧缓冲区并把两个缓冲区进行交换后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂现象,如下图:



20210624150225240.png


为了解决这个问题,GPU 通常有一个机制叫做垂直同步(简写也是 V-Sync),当开启垂直同步后,GPU 会等待显示器的 VSync 信号发出后,才进行新的一帧渲染和缓冲区更新。这样能解决画面撕裂现象,也增加了画面流畅度,但需要消费更多的计算资源,也会带来部分延迟。



4. 第四次迭代:任务饿死


某个状态下,一直有新的高优先级的任务加入到队列中,这样就会导致其他低优先级的任务得不到执行,这称为任务饿死。


Chromium 为了解决任务饿死的问题,给每个队列设置了执行权重,也就是如果连续执行了一定个数的高优先级的任务,那么中间会执行一次低优先级的任务,这样就缓解了任务饿死的情况。



参考资料

BlinkOn Scheduler Presentation


2021062417195526.png



Blink Scheduler

20210624172047603.png


Chrome 的消息类型


2021062417250548.png


task_type.h 如下:

// Copyright 2017 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_PUBLIC_PLATFORM_TASK_TYPE_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_TASK_TYPE_H_
namespace blink {
// A list of task sources known to Blink according to the spec.
// This enum is used for a histogram and it should not be re-numbered.
//
// For the task type usage guideline, see https://bit.ly/2vMAsQ4
//
// When a new task type is created:
// * use kCount value as a new value,
// * in tools/metrics/histograms/enums.xml update the
//   "RendererSchedulerTaskType" enum
// * update TaskTypes.md
enum class TaskType : unsigned char {
  ///
  // Speced tasks should use one of the following task types
  ///
  // Speced tasks and related internal tasks should be posted to one of
  // the following task runners. These task runners may be throttled.
  // This value is used as a default value in cases where TaskType
  // isn't supported yet. Don't use outside platform/scheduler code.
  kDeprecatedNone = 0,
  // https://html.spec.whatwg.org/multipage/webappapis.html#generic-task-sources
  //
  // This task source is used for features that react to DOM manipulations, such
  // as things that happen in a non-blocking fashion when an element is inserted
  // into the document.
  kDOMManipulation = 1,
  // This task source is used for features that react to user interaction, for
  // example keyboard or mouse input. Events sent in response to user input
  // (e.g. click events) must be fired using tasks queued with the user
  // interaction task source.
  kUserInteraction = 2,
  // TODO(altimin) Fix the networking task source related namings once it is
  // clear how
  // all loading tasks are annotated.
  // This task source is used for features that trigger in response to network
  // activity.
  kNetworking = 3,
  // This is a part of Networking task source used to annotate tasks which are
  // posted from the loading stack (i.e. WebURLLoader).
  kNetworkingWithURLLoaderAnnotation = 50,
  // This is a part of Networking task that should not be frozen when a page is
  // frozen.
  kNetworkingUnfreezable = 75,
  // This task source is used for control messages between kNetworking tasks.
  kNetworkingControl = 4,
  // This task source is used to queue calls to history.back() and similar APIs.
  kHistoryTraversal = 5,
  // https://html.spec.whatwg.org/multipage/embedded-content.html#the-embed-element
  // This task source is used for the embed element setup steps.
  kEmbed = 6,
  // https://html.spec.whatwg.org/multipage/embedded-content.html#media-elements
  // This task source is used for all tasks queued in the [Media elements]
  // section and subsections of the spec unless explicitly specified otherwise.
  kMediaElementEvent = 7,
  // https://html.spec.whatwg.org/multipage/scripting.html#the-canvas-element
  // This task source is used to invoke the result callback of
  // HTMLCanvasElement.toBlob().
  kCanvasBlobSerialization = 8,
  // https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
  // This task source is used when an algorithm requires a microtask to be
  // queued.
  kMicrotask = 9,
  // https://html.spec.whatwg.org/multipage/webappapis.html#timers
  // For tasks queued by setTimeout() or setInterval().
  //
  // Task nesting level is < 5 and timeout is zero.
  kJavascriptTimerImmediate = 72,
  // Task nesting level is < 5 and timeout is > 0.
  kJavascriptTimerDelayedLowNesting = 73,
  // Task nesting level is >= 5.
  kJavascriptTimerDelayedHighNesting = 10,
  // Note: The timeout is increased to be at least 4ms when the task nesting
  // level is >= 5. Therefore, the timeout is necessarily > 0 for
  // kJavascriptTimerDelayedHighNesting.
  // https://html.spec.whatwg.org/multipage/comms.html#sse-processing-model
  // This task source is used for any tasks that are queued by EventSource
  // objects.
  kRemoteEvent = 11,
  // https://html.spec.whatwg.org/multipage/comms.html#feedback-from-the-protocol
  // The task source for all tasks queued in the [WebSocket] section of the
  // spec.
  kWebSocket = 12,
  // https://html.spec.whatwg.org/multipage/comms.html#web-messaging
  // This task source is used for the tasks in cross-document messaging.
  kPostedMessage = 13,
  // https://html.spec.whatwg.org/multipage/comms.html#message-ports
  kUnshippedPortMessage = 14,
  // https://www.w3.org/TR/FileAPI/#blobreader-task-source
  // This task source is used for all tasks queued in the FileAPI spec to read
  // byte sequences associated with Blob and File objects.
  kFileReading = 15,
  // https://www.w3.org/TR/IndexedDB/#request-api
  kDatabaseAccess = 16,
  // https://w3c.github.io/presentation-api/#common-idioms
  // This task source is used for all tasks in the Presentation API spec.
  kPresentation = 17,
  // https://www.w3.org/TR/2016/WD-generic-sensor-20160830/#sensor-task-source
  // This task source is used for all tasks in the Sensor API spec.
  kSensor = 18,
  // https://w3c.github.io/performance-timeline/#performance-timeline
  kPerformanceTimeline = 19,
  // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15
  // This task source is used for all tasks in the WebGL spec.
  kWebGL = 20,
  // https://www.w3.org/TR/requestidlecallback/#start-an-event-loop-s-idle-period
  kIdleTask = 21,
  // Use MiscPlatformAPI for a task that is defined in the spec but is not yet
  // associated with any specific task runner in the spec. MiscPlatformAPI is
  // not encouraged for stable and matured APIs. The spec should define the task
  // runner explicitly.
  // The task runner may be throttled.
  kMiscPlatformAPI = 22,
  // Tasks used for DedicatedWorker's requestAnimationFrame.
  kWorkerAnimation = 51,
  // Obsolete.
  // kExperimentalWebSchedulingUserInteraction = 53,
  // kExperimentalWebSchedulingBestEffort = 54,
  // https://drafts.csswg.org/css-font-loading/#task-source
  kFontLoading = 56,
  // https://w3c.github.io/manifest/#dfn-application-life-cycle-task-source
  kApplicationLifeCycle = 57,
  // https://wicg.github.io/background-fetch/#infrastructure
  kBackgroundFetch = 58,
  // https://www.w3.org/TR/permissions/
  kPermission = 59,
  // https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
  kServiceWorkerClientMessage = 60,
  // https://wicg.github.io/web-locks/#web-locks-tasks-source
  kWebLocks = 66,
  // https://w3c.github.io/screen-wake-lock/#dfn-screen-wake-lock-task-source
  kWakeLock = 76,
  ///
  // Not-speced tasks should use one of the following task types
  ///
  // The default task type. The task may be throttled or paused.
  kInternalDefault = 23,
  // Tasks used for all tasks associated with loading page content.
  kInternalLoading = 24,
  // Tasks for tests or mock objects.
  kInternalTest = 26,
  // Tasks that are posting back the result from the WebCrypto task runner to
  // the Blink thread that initiated the call and holds the Promise. Tasks with
  // this type are posted by:
  // * //components/webcrypto
  kInternalWebCrypto = 27,
  // Tasks to execute media-related things like logging or playback. Tasks with
  // this type are mainly posted by:
  // * //content/renderer/media
  // * //media
  kInternalMedia = 29,
  // Tasks to execute things for real-time media processing like recording. If a
  // task touches MediaStreamTracks, associated sources/sinks, and Web Audio,
  // this task type should be used.
  // Tasks with this type are mainly posted by:
  // * //content/renderer/media
  // * //media
  // * blink/renderer/modules/webaudio
  // * blink/public/platform/audio
  kInternalMediaRealTime = 30,
  // Tasks related to user interaction like clicking or inputting texts.
  kInternalUserInteraction = 32,
  // Tasks related to the inspector.
  kInternalInspector = 33,
  // Obsolete.
  // kInternalWorker = 36,
  // Translation task that freezes when the frame is not visible.
  kInternalTranslation = 55,
  // Tasks used at IntersectionObserver.
  kInternalIntersectionObserver = 44,
  // Task used for ContentCapture.
  kInternalContentCapture = 61,
  // Navigation tasks and tasks which have to run in order with them, including
  // legacy IPCs and channel associated interfaces.
  // Note that the ordering between tasks related to different frames is not
  // always guaranteed - tasks belonging to different frames can be reordered
  // when one of the frames is frozen.
  // Note: all AssociatedRemotes/AssociatedReceivers should use this task type.
  kInternalNavigationAssociated = 63,
  // Tasks which should run when the frame is frozen, but otherwise should run
  // in order with other legacy IPC and channel-associated interfaces.
  // Only tasks related to unfreezing itself should run here, the majority of
  // the tasks
  // should use kInternalNavigationAssociated instead.
  kInternalNavigationAssociatedUnfreezable = 64,
  // Task used to split a script loading task for cooperative scheduling
  kInternalContinueScriptLoading = 65,
  // Experimental tasks types used for main thread scheduling postTask API
  // (https://github.com/WICG/main-thread-scheduling).
  // These task types should not be passed directly to
  // FrameScheduler::GetTaskRunner(); they are used indirectly by
  // WebSchedulingTaskQueues.
  kExperimentalWebScheduling = 67,
  // Tasks used to control frame lifecycle - they should run even when the frame
  // is frozen.
  kInternalFrameLifecycleControl = 68,
  // Tasks used for find-in-page.
  kInternalFindInPage = 70,
  // Tasks that come in on the HighPriorityLocalFrame interface.
  kInternalHighPriorityLocalFrame = 71,
  // Tasks that are should use input priority task queue/runner.
  kInternalInputBlocking = 77,
  // Tasks related to the WebGPU API
  kWebGPU = 78,
  ///
  // The following task types are only for thread-local queues.
  ///
  // The following task types are internal-use only, escpecially for annotations
  // like UMA of per-thread task queues. Do not specify these task types when to
  // get a task queue/runner.
  kMainThreadTaskQueueV8 = 37,
  kMainThreadTaskQueueCompositor = 38,
  kMainThreadTaskQueueDefault = 39,
  kMainThreadTaskQueueInput = 40,
  kMainThreadTaskQueueIdle = 41,
  // Removed:
  // kMainThreadTaskQueueIPC = 42,
  kMainThreadTaskQueueControl = 43,
  // Removed:
  // kMainThreadTaskQueueCleanup = 52,
  kMainThreadTaskQueueMemoryPurge = 62,
  kMainThreadTaskQueueNonWaking = 69,
  kMainThreadTaskQueueIPCTracking = 74,
  kCompositorThreadTaskQueueDefault = 45,
  kCompositorThreadTaskQueueInput = 49,
  kWorkerThreadTaskQueueDefault = 46,
  kWorkerThreadTaskQueueV8 = 47,
  kWorkerThreadTaskQueueCompositor = 48,
  kCount = 79,
};
}  // namespace blink
#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_TASK_TYPE_H_



Chrome 消息优先级


20210624173120519.png



task_queue.h 如下:

// Copyright 2018 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 BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_
#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_
#include <memory>
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/task/common/checked_lock.h"
#include "base/task/sequence_manager/lazy_now.h"
#include "base/task/sequence_manager/tasks.h"
#include "base/task/task_observer.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/trace_event/base_tracing_forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
class TaskObserver;
namespace trace_event {
class BlameContext;
}
namespace sequence_manager {
namespace internal {
class AssociatedThreadId;
class SequenceManagerImpl;
class TaskQueueImpl;
}  // namespace internal
class TimeDomain;
// TODO(kraynov): Make TaskQueue to actually be an interface for TaskQueueImpl
// and stop using ref-counting because we're no longer tied to task runner
// lifecycle and there's no other need for ref-counting either.
// NOTE: When TaskQueue gets automatically deleted on zero ref-count,
// TaskQueueImpl gets gracefully shutdown. It means that it doesn't get
// unregistered immediately and might accept some last minute tasks until
// SequenceManager will unregister it at some point. It's done to ensure that
// task queue always gets unregistered on the main thread.
class BASE_EXPORT TaskQueue : public RefCountedThreadSafe<TaskQueue> {
 public:
  class Observer {
   public:
    virtual ~Observer() = default;
    // Notify observer that the time at which this queue wants to run
    // the next task has changed. |next_wakeup| can be in the past
    // (e.g. TimeTicks() can be used to notify about immediate work).
    // Can be called on any thread
    // All methods but SetObserver, SetTimeDomain and GetTimeDomain can be
    // called on |queue|.
    //
    // TODO(altimin): Make it absl::optional<TimeTicks> to tell
    // observer about cancellations.
    virtual void OnQueueNextWakeUpChanged(TimeTicks next_wake_up) = 0;
  };
  // Shuts down the queue. All tasks currently queued will be discarded.
  virtual void ShutdownTaskQueue();
  // Shuts down the queue when there are no more tasks queued.
  void ShutdownTaskQueueGracefully();
  // Queues with higher priority are selected to run before queues of lower
  // priority. Note that there is no starvation protection, i.e., a constant
  // stream of high priority work can mean that tasks in lower priority queues
  // won't get to run.
  // TODO(scheduler-dev): Could we define a more clear list of priorities?
  // See https://crbug.com/847858.
  enum QueuePriority : uint8_t {
    // Queues with control priority will run before any other queue, and will
    // explicitly starve other queues. Typically this should only be used for
    // private queues which perform control operations.
    kControlPriority = 0,
    kHighestPriority = 1,
    kVeryHighPriority = 2,
    kHighPriority = 3,
    kNormalPriority = 4,  // Queues with normal priority are the default.
    kLowPriority = 5,
    // Queues with best effort priority will only be run if all other queues are
    // empty.
    kBestEffortPriority = 6,
    // Must be the last entry.
    kQueuePriorityCount = 7,
    kFirstQueuePriority = kControlPriority,
  };
  // Can be called on any thread.
  static const char* PriorityToString(QueuePriority priority);
  // Options for constructing a TaskQueue.
  struct Spec {
    explicit Spec(const char* name) : name(name) {}
    Spec SetShouldMonitorQuiescence(bool should_monitor) {
      should_monitor_quiescence = should_monitor;
      return *this;
    }
    Spec SetShouldNotifyObservers(bool run_observers) {
      should_notify_observers = run_observers;
      return *this;
    }
    // Delayed fences require Now() to be sampled when posting immediate tasks
    // which is not free.
    Spec SetDelayedFencesAllowed(bool allow_delayed_fences) {
      delayed_fence_allowed = allow_delayed_fences;
      return *this;
    }
    Spec SetTimeDomain(TimeDomain* domain) {
      time_domain = domain;
      return *this;
    }
    const char* name;
    bool should_monitor_quiescence = false;
    TimeDomain* time_domain = nullptr;
    bool should_notify_observers = true;
    bool delayed_fence_allowed = false;
  };
  // TODO(altimin): Make this private after TaskQueue/TaskQueueImpl refactoring.
  TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
            const TaskQueue::Spec& spec);
  TaskQueue(const TaskQueue&) = delete;
  TaskQueue& operator=(const TaskQueue&) = delete;
  // Information about task execution.
  //
  // Wall-time related methods (start_time, end_time, wall_duration) can be
  // called only when |has_wall_time()| is true.
  // Thread-time related mehtods (start_thread_time, end_thread_time,
  // thread_duration) can be called only when |has_thread_time()| is true.
  //
  // start_* should be called after RecordTaskStart.
  // end_* and *_duration should be called after RecordTaskEnd.
  class BASE_EXPORT TaskTiming {
   public:
    enum class State { NotStarted, Running, Finished };
    enum class TimeRecordingPolicy { DoRecord, DoNotRecord };
    TaskTiming(bool has_wall_time, bool has_thread_time);
    bool has_wall_time() const { return has_wall_time_; }
    bool has_thread_time() const { return has_thread_time_; }
    base::TimeTicks start_time() const {
      DCHECK(has_wall_time());
      return start_time_;
    }
    base::TimeTicks end_time() const {
      DCHECK(has_wall_time());
      return end_time_;
    }
    base::TimeDelta wall_duration() const {
      DCHECK(has_wall_time());
      return end_time_ - start_time_;
    }
    base::ThreadTicks start_thread_time() const {
      DCHECK(has_thread_time());
      return start_thread_time_;
    }
    base::ThreadTicks end_thread_time() const {
      DCHECK(has_thread_time());
      return end_thread_time_;
    }
    base::TimeDelta thread_duration() const {
      DCHECK(has_thread_time());
      return end_thread_time_ - start_thread_time_;
    }
    State state() const { return state_; }
    void RecordTaskStart(LazyNow* now);
    void RecordTaskEnd(LazyNow* now);
    // Protected for tests.
   protected:
    State state_ = State::NotStarted;
    bool has_wall_time_;
    bool has_thread_time_;
    base::TimeTicks start_time_;
    base::TimeTicks end_time_;
    base::ThreadTicks start_thread_time_;
    base::ThreadTicks end_thread_time_;
  };
  // An interface that lets the owner vote on whether or not the associated
  // TaskQueue should be enabled.
  class BASE_EXPORT QueueEnabledVoter {
   public:
    ~QueueEnabledVoter();
    QueueEnabledVoter(const QueueEnabledVoter&) = delete;
    const QueueEnabledVoter& operator=(const QueueEnabledVoter&) = delete;
    // Votes to enable or disable the associated TaskQueue. The TaskQueue will
    // only be enabled if all the voters agree it should be enabled, or if there
    // are no voters.
    // NOTE this must be called on the thread the associated TaskQueue was
    // created on.
    void SetVoteToEnable(bool enabled);
    bool IsVotingToEnable() const { return enabled_; }
   private:
    friend class TaskQueue;
    explicit QueueEnabledVoter(scoped_refptr<TaskQueue> task_queue);
    scoped_refptr<TaskQueue> const task_queue_;
    bool enabled_;
  };
  // Returns an interface that allows the caller to vote on whether or not this
  // TaskQueue is enabled. The TaskQueue will be enabled if there are no voters
  // or if all agree it should be enabled.
  // NOTE this must be called on the thread this TaskQueue was created by.
  std::unique_ptr<QueueEnabledVoter> CreateQueueEnabledVoter();
  // NOTE this must be called on the thread this TaskQueue was created by.
  bool IsQueueEnabled() const;
  // Returns true if the queue is completely empty.
  bool IsEmpty() const;
  // Returns the number of pending tasks in the queue.
  size_t GetNumberOfPendingTasks() const;
  // Returns true if the queue has work that's ready to execute now.
  // NOTE: this must be called on the thread this TaskQueue was created by.
  bool HasTaskToRunImmediately() const;
  // Returns requested run time of next scheduled wake-up for a delayed task
  // which is not ready to run. If there are no such tasks (immediate tasks
  // don't count) or the queue is disabled it returns nullopt.
  // NOTE: this must be called on the thread this TaskQueue was created by.
  absl::optional<TimeTicks> GetNextScheduledWakeUp();
  // Can be called on any thread.
  virtual const char* GetName() const;
  // Serialise this object into a trace.
  void WriteIntoTrace(perfetto::TracedValue context) const;
  // Set the priority of the queue to |priority|. NOTE this must be called on
  // the thread this TaskQueue was created by.
  void SetQueuePriority(QueuePriority priority);
  // Returns the current queue priority.
  QueuePriority GetQueuePriority() const;
  // These functions can only be called on the same thread that the task queue
  // manager executes its tasks on.
  void AddTaskObserver(TaskObserver* task_observer);
  void RemoveTaskObserver(TaskObserver* task_observer);
  // Set the blame context which is entered and left while executing tasks from
  // this task queue. |blame_context| must be null or outlive this task queue.
  // Must be called on the thread this TaskQueue was created by.
  void SetBlameContext(trace_event::BlameContext* blame_context);
  // Removes the task queue from the previous TimeDomain and adds it to
  // |domain|.  This is a moderately expensive operation.
  void SetTimeDomain(TimeDomain* domain);
  // Returns the queue's current TimeDomain.  Can be called from any thread.
  TimeDomain* GetTimeDomain() const;
  enum class InsertFencePosition {
    kNow,  // Tasks posted on the queue up till this point further may run.
           // All further tasks are blocked.
    kBeginningOfTime,  // No tasks posted on this queue may run.
  };
  // Inserts a barrier into the task queue which prevents tasks with an enqueue
  // order greater than the fence from running until either the fence has been
  // removed or a subsequent fence has unblocked some tasks within the queue.
  // Note: delayed tasks get their enqueue order set once their delay has
  // expired, and non-delayed tasks get their enqueue order set when posted.
  //
  // Fences come in three flavours:
  // - Regular (InsertFence(NOW)) - all tasks posted after this moment
  //   are blocked.
  // - Fully blocking (InsertFence(kBeginningOfTime)) - all tasks including
  //   already posted are blocked.
  // - Delayed (InsertFenceAt(timestamp)) - blocks all tasks posted after given
  //   point in time (must be in the future).
  //
  // Only one fence can be scheduled at a time. Inserting a new fence
  // will automatically remove the previous one, regardless of fence type.
  void InsertFence(InsertFencePosition position);
  // Delayed fences are only allowed for queues created with
  // SetDelayedFencesAllowed(true) because this feature implies sampling Now()
  // (which isn't free) for every PostTask, even those with zero delay.
  void InsertFenceAt(TimeTicks time);
  // Removes any previously added fence and unblocks execution of any tasks
  // blocked by it.
  void RemoveFence();
  // Returns true if the queue has a fence but it isn't necessarily blocking
  // execution of tasks (it may be the case if tasks enqueue order hasn't
  // reached the number set for a fence).
  bool HasActiveFence();
  // Returns true if the queue has a fence which is blocking execution of tasks.
  bool BlockedByFence() const;
  void SetObserver(Observer* observer);
  // Controls whether or not the queue will emit traces events when tasks are
  // posted to it while disabled. This only applies for the current or next
  // period during which the queue is disabled. When the queue is re-enabled
  // this will revert back to the default value of false.
  void SetShouldReportPostedTasksWhenDisabled(bool should_report);
  // Create a task runner for this TaskQueue which will annotate all
  // posted tasks with the given task type.
  // May be called on any thread.
  // NOTE: Task runners don't hold a reference to a TaskQueue, hence,
  // it's required to retain that reference to prevent automatic graceful
  // shutdown. Unique ownership of task queues will fix this issue soon.
  scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner(TaskType task_type);
  // Default task runner which doesn't annotate tasks with a task type.
  const scoped_refptr<SingleThreadTaskRunner>& task_runner() const {
    return default_task_runner_;
  }
  // Checks whether or not this TaskQueue has a TaskQueueImpl.
  // TODO(kdillon): Remove this method when TaskQueueImpl inherits from
  // TaskQueue and TaskQueue no longer owns an Impl.
  bool HasImpl() { return !!impl_; }
  using OnTaskStartedHandler =
      RepeatingCallback<void(const Task&, const TaskQueue::TaskTiming&)>;
  using OnTaskCompletedHandler =
      RepeatingCallback<void(const Task&, TaskQueue::TaskTiming*, LazyNow*)>;
  using OnTaskPostedHandler = RepeatingCallback<void(const Task&)>;
  // Sets a handler to subscribe for notifications about started and completed
  // tasks.
  void SetOnTaskStartedHandler(OnTaskStartedHandler handler);
  // |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);
  // Set 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.
  void SetOnTaskPostedHandler(OnTaskPostedHandler handler);
  base::WeakPtr<TaskQueue> AsWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }
 protected:
  virtual ~TaskQueue();
  internal::TaskQueueImpl* GetTaskQueueImpl() const { return impl_.get(); }
 private:
  friend class RefCountedThreadSafe<TaskQueue>;
  friend class internal::SequenceManagerImpl;
  friend class internal::TaskQueueImpl;
  void AddQueueEnabledVoter(bool voter_is_enabled);
  void RemoveQueueEnabledVoter(bool voter_is_enabled);
  bool AreAllQueueEnabledVotersEnabled() const;
  void OnQueueEnabledVoteChanged(bool enabled);
  bool IsOnMainThread() const;
  // TaskQueue has ownership of an underlying implementation but in certain
  // cases (e.g. detached frames) their lifetime may diverge.
  // This method should be used to take away the impl for graceful shutdown.
  // TaskQueue will disregard any calls or posting tasks thereafter.
  std::unique_ptr<internal::TaskQueueImpl> TakeTaskQueueImpl();
  // |impl_| can be written to on the main thread but can be read from
  // any thread.
  // |impl_lock_| must be acquired when writing to |impl_| or when accessing
  // it from non-main thread. Reading from the main thread does not require
  // a lock.
  mutable base::internal::CheckedLock impl_lock_{
      base::internal::UniversalPredecessor{}};
  std::unique_ptr<internal::TaskQueueImpl> impl_;
  const WeakPtr<internal::SequenceManagerImpl> sequence_manager_;
  scoped_refptr<internal::AssociatedThreadId> associated_thread_;
  scoped_refptr<SingleThreadTaskRunner> default_task_runner_;
  int enabled_voter_count_ = 0;
  int voter_count_ = 0;
  const char* name_;
  base::WeakPtrFactory<TaskQueue> weak_ptr_factory_{this};
};
}  // namespace sequence_manager
}  // namespace base
#endif  // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_


Headless Chrome


20210624174037143.png

























相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
目录
相关文章
|
1天前
|
存储 缓存 前端开发
浏览器缓存工作原理是什么?
浏览器缓存工作原理是什么?
|
1天前
|
存储 安全 前端开发
浏览器跨窗口通信:原理与实践
浏览器跨窗口通信:原理与实践
56 0
|
1天前
|
消息中间件 JavaScript 前端开发
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
|
1天前
|
消息中间件 前端开发 Java
【面试题】前端必修-浏览器的渲染原理
【面试题】前端必修-浏览器的渲染原理
|
5月前
|
Web App开发 JavaScript 前端开发
从浏览器原理出发聊聊Chrome插件
本文从浏览器架构演进、插件运行机制、插件基本介绍和一些常见的插件实现思路几个方向聊聊Chrome插件。
790 0
|
8月前
|
安全 算法 网络协议
浏览器基础原理-安全: HTTPS
浏览器基础原理-安全: HTTPS
63 0
|
8月前
|
Web App开发 存储 监控
浏览器基础原理-安全: 渲染进程-安全沙盒
浏览器基础原理-安全: 渲染进程-安全沙盒
39 0
|
8月前
|
安全
浏览器基础原理-安全: CSRF攻击
浏览器基础原理-安全: CSRF攻击
56 0
|
8月前
|
JavaScript 安全 前端开发
浏览器基础原理-安全: 同源策略
浏览器基础原理-安全: 同源策略
39 0
|
8月前
|
存储 编解码 安全
浏览器基础原理-安全: 跨站脚本攻击(XSS)
浏览器基础原理-安全: 跨站脚本攻击(XSS)
34 0