File: performance_scenarios.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (211 lines) | stat: -rw-r--r-- 8,533 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIOS_H_
#define COMPONENTS_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIOS_H_

#include <atomic>
#include <utility>

#include "base/component_export.h"
#include "base/containers/enum_set.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/scoped_refptr.h"
#include "components/performance_manager/scenario_api/performance_scenario_memory_forward.h"

namespace performance_scenarios {

// Defines performance scenarios that a page can be in.
//
// Each enum is a list of mutually-exclusive scenarios. The complete scenario
// state is a tuple of all scenarios that are detected, at most one from each
// enum.
//
// The browser process detects which scenarios apply and shares that state with
// child processes over shared memory. Each process can view a global scenario
// list over the entire browser (eg. some page is loading) or a scenario list
// targeted only to that process (eg. a page hosted in this process is loading).
//
// Additional functions to let the browser process query the performance
// scenarios for a child process are in
// components/performance_manager/public/scenarios/process_performance_scenarios.h.

// Scenarios indicating a page is loading, ordered from least-specific to
// most-specific.
enum class LoadingScenario {
  // No pages covered by the scenario are loading.
  kNoPageLoading = 0,
  // No visible pages are loading, but a non-visible page is.
  kBackgroundPageLoading,
  // The focused page (if any) is not loading, but a visible page is.
  kVisiblePageLoading,
  // The focused page is loading. Implies the page is also visible.
  kFocusedPageLoading,
  kMax = kFocusedPageLoading,
};
using LoadingScenarios = base::EnumSet<LoadingScenario,
                                       LoadingScenario::kNoPageLoading,
                                       LoadingScenario::kMax>;

// Scenarios indicating user input, ordered from least-specific to
// most-specific.
enum class InputScenario {
  // No input was detected.
  kNoInput = 0,
  // The user is typing in a focused page. There were no recent taps or scrolls.
  kTyping,
  // The user tapped a focused page. There may be recent typing, but not
  // scrolls.
  kTap,
  // The user is scrolling in a focused page. There may also be recent typing or
  // taps.
  kScroll,
  kMax = kScroll,
};
using InputScenarios =
    base::EnumSet<InputScenario, InputScenario::kNoInput, InputScenario::kMax>;

// The scope that a scenario covers.
enum class ScenarioScope {
  // The scenario covers only pages hosted in the current process.
  kCurrentProcess,
  // The scenario covers the whole browser.
  kGlobal,
};
using ScenarioScopes = base::EnumSet<ScenarioScope,
                                     /*Min=*/ScenarioScope::kCurrentProcess,
                                     /*Max=*/ScenarioScope::kGlobal>;

// Different subsets of scenarios that can be checked with the ScenariosMatch()
// function or a MatchingScenarioObserver.
//
// A given ScenarioScope `scope` matches a ScenarioPattern if all of:
//
// * GetLoadingScenario(scope) returns a value in the `loading` set, or the set
//   is empty.
// * GetInputScenarios(scope) returns a value in the `input` set, or the set is
//   empty.
struct COMPONENT_EXPORT(SCENARIO_API) ScenarioPattern {
  // Set of LoadingScenarios that match the pattern. If this is empty, any
  // LoadingScenario matches.
  LoadingScenarios loading;

  // Set of InputScenarios that match the pattern. If this is empty, any
  // InputScenario matches.
  InputScenarios input;
};

// A ScenarioPattern for a scope that's considered "idle": only background pages
// are loading and there is no input. This is a good definition of "idle" for
// most purposes, but some features that are particularly sensitive to different
// scenarios may want to define a different ScenarioPattern.
inline constexpr ScenarioPattern kDefaultIdleScenarios{
    .loading = {LoadingScenario::kNoPageLoading,
                LoadingScenario::kBackgroundPageLoading},
    .input = {InputScenario::kNoInput},
};

// A wrapper around a std::atomic<T> that's stored in shared memory. The wrapper
// prevents the shared memory from being unmapped while a caller has a reference
// to the atomic. Dereference the SharedAtomicRef to read from it as a
// std::atomic. See the comments above GetLoadingScenario() for usage notes.
template <typename T>
class SharedAtomicRef {
 public:
  SharedAtomicRef(scoped_refptr<RefCountedScenarioMapping> mapping,
                  const std::atomic<T>& wrapped_atomic)
      : mapping_(std::move(mapping)), wrapped_atomic_(wrapped_atomic) {}

  ~SharedAtomicRef() = default;

  // Move-only.
  SharedAtomicRef(const SharedAtomicRef&) = delete;
  SharedAtomicRef& operator=(const SharedAtomicRef&) = delete;
  SharedAtomicRef(SharedAtomicRef&&) = default;
  SharedAtomicRef& operator=(SharedAtomicRef&&) = default;

  // Smart-pointer-like interface:

  // Returns a pointer to the wrapped atomic.
  const std::atomic<T>* get() const { return &wrapped_atomic_; }

  // Returns a reference to the wrapped atomic.
  const std::atomic<T>& operator*() const { return wrapped_atomic_; }

  // Returns a pointer to the wrapped atomic for method invocation.
  const std::atomic<T>* operator->() const { return &wrapped_atomic_; }

 private:
  const scoped_refptr<RefCountedScenarioMapping> mapping_;

  // A reference into `mapping_`, not PartitionAlloc memory.
  RAW_PTR_EXCLUSION const std::atomic<T>& wrapped_atomic_;
};

// Functions to query performance scenarios.
//
// Since the scenarios can be modified at any time from another process, they're
// accessed through SharedAtomicRef. Get a snapshot of the scenario with
// std::atomic::load(). std::memory_order_relaxed is usually sufficient since no
// other memory depends on the scenario value.
//
// Usage:
//
//   // Test whether any foreground page is loading.
//   LoadingScenario scenario = GetLoadingScenario(ScenarioScope::kGlobal)
//                                 ->load(std::memory_order_relaxed);
//   if (scenario == LoadingScenario::kFocusedPageLoading ||
//       scenario == LoadingScenario::kVisiblePageLoading) {
//     ... delay less-important work until scenario changes ...
//   }
//
//   // Inverse of the above test: true if NO foreground page is loading.
//   if (CurrentScenariosMatch(ScenarioScope::kGlobal,
//                             ScenarioPattern{.loading = {
//                               LoadingScenario::kNoPageLoading,
//                               LoadingScenario::kBackgroundPageLoading,
//                             }) {
//     ... good time to do less-important work ...
//   }
//
//   // Test whether the current process is in the critical path for user input.
//   if (GetInputScenario(ScenarioScope::kCurrentProcess)->load(
//           std::memory_order_relaxed) != InputScenario::kNoInput) {
//     ... current process should prioritize input responsiveness ...
//   }
//
//   // Equivalently:
//   if (!CurrentScenariosMatch(ScenarioScope::kCurrentProcess,
//                              ScenarioPattern{
//                                .input = {InputScenario::kNoInput}
//                              }) {
//     ... current process should prioritize input responsiveness ...
//   }
//
//   // Test whether the browser overall is idle by the most common definition.
//   if (CurrentScenariosMatch(ScenarioScope::kGlobal, kDefaultIdleScenarios)) {
//     ... good time to do maintenance tasks ...
//   }

// Returns a reference to the loading scenario for `scope`.
COMPONENT_EXPORT(SCENARIO_API)
SharedAtomicRef<LoadingScenario> GetLoadingScenario(ScenarioScope scope);

// Returns a reference to the input scenario for `scope`.
COMPONENT_EXPORT(SCENARIO_API)
SharedAtomicRef<InputScenario> GetInputScenario(ScenarioScope scope);

// Returns true if `scope` currently matches `pattern`.
COMPONENT_EXPORT(SCENARIO_API)
bool CurrentScenariosMatch(ScenarioScope scope, ScenarioPattern pattern);

// Returns true if the given scenarios match `pattern`.
COMPONENT_EXPORT(SCENARIO_API)
bool ScenariosMatch(LoadingScenario loading_scenario,
                    InputScenario input_scenario,
                    ScenarioPattern pattern);

}  // namespace performance_scenarios

#endif  // COMPONENTS_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIOS_H_