blob: b22f42209519e808f76d853a076cc63eab9a41c6 [file] [view]
The **PriorityVotingSystem** is responsible for assigning the
priority of the votable nodes in the graph.
This is done through a system of voting where each voter can independently
increase the priority of a graph node, and the vote with the highest
priority determines which priority that graph node will be assigned.
A **VoteContext** identifies the graph node that a vote targets. It is a
`std::variant` that explicitly lists the votable node types (currently
`FrameNode`, `WorkerNode`, `PageNode` and `ProcessNode`). Voting on any other
node type fails to compile.
## Vote flow
```
Frames
======
PageToFrameVoteExpander -+
+-> MaxVoteAggregator -> PrioritySetter -> FrameNode -> ProcessPriorityAggregator
Other voters -+
Workers
=======
Voters -> MaxVoteAggregator -> PrioritySetter -> WorkerNode -> ProcessPriorityAggregator
Processes
=========
ProcessPriorityAggregator -+
+-> MaxVoteAggregator -> PrioritySetter -> ProcessNode
Other voters -+
Pages
=====
Voters -> MaxVoteAggregator -> PageToFrameVoteExpander
```
Every vote goes into a single **MaxVoteAggregator**, through a voting channel.
The top vote of a node in the aggregator is therefore its final priority.
Each voter tracks a single property of a vote context and casts their vote
via their voting channel to the MaxVoteAggregator.
The observers of the MaxVoteAggregator are:
- The **PrioritySetter** assigns the top vote of frames, workers and (renderer)
processes to their priority, or their default priority when they have no
vote.
- The **PageToFrameVoteExpander** casts the top vote of pages on every frame of
the page, back into the MaxVoteAggregator, where it is aggregated with the
frames' own votes.
The **ProcessPriorityAggregator** follows the priority of every frame and worker
(as set by the PrioritySetter), and casts the highest priority of the frames
and workers hosted by a process as a vote on that process, where it is
aggregated with the votes cast directly on the process. This vote is cast even
when it is the lowest priority, so that a process drops from its initial high
priority once it hosts a frame or worker.
The **PriorityVotingSystem** owns the MaxVoteAggregator, and builds each of
these from it. It also verifies that no votes are leaked.
## Invariants
- **No cycles:** Derived votes only flow from pages to frames, and from frames
and workers to processes. Processes never cast votes. The MaxVoteAggregator
CHECKs that the top vote for a node doesn't change while its observers are
being notified, which catches any cycle.
- **Re-entrancy:** Votes can be cast while the MaxVoteAggregator is notifying
its observers, both by voters (e.g. the InheritParentPriorityVoter votes on
child frames when the PrioritySetter sets the priority of a frame) and by the
PageToFrameVoteExpander and the ProcessPriorityAggregator, so the aggregator
supports being re-entered.
- **Timing:** The PriorityVotingSystem must be added to the graph before any
node (this is CHECKed), so that the ProcessPriorityAggregator sees every frame
and worker being added. The priority of a frame or worker is taken into
account in the vote on its process once it is added to the graph, and is
followed until it is removed.
Page votes can't change while a frame of the page is being removed (this is
CHECKed).
- **No leaks:** Voters must remove their votes on a node before it is removed
from the graph (this is CHECKed).
## Voters vs. derived votes
Some voters (e.g. **InheritClientPriorityVoter**) also derive votes from the
priority of other nodes. They are voters because they implement an optional
policy. The derived votes cast on frames (from their page) and on processes
(from their frames and workers) are part of the voting system itself: they are
always needed for the priorities to be correct.