-
-
Notifications
You must be signed in to change notification settings - Fork 359
Multi-instance <TimeToInitialDisplay> / <TimeToFullDisplay> coordination; a multi-signal TTID/TTFD system
#6090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
alwx
wants to merge
6
commits into
main
Choose a base branch
from
alwx/enhancement/multiple-ttid-ttfd
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
9b9178c
Coordinated TTID/TTFD
alwx f156953
Changelog entry
alwx cb268c8
Added changelog PR number
alwx aa62b62
Updates the order of entries in CHANGELOG.md
alwx 0598e1b
useRef instead of useId for React 17 compatibility
alwx a9937e9
Use refs to only throw warnings once
alwx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
packages/core/src/js/tracing/timeToDisplayCoordinator.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /** | ||
| * Coordinator for multi-instance `<TimeToInitialDisplay>` / `<TimeToFullDisplay>` | ||
| * components on a single screen (active span). | ||
| */ | ||
|
|
||
| type Checkpoint = { ready: boolean }; | ||
| type Listener = () => void; | ||
|
|
||
| interface SpanRegistry { | ||
| checkpoints: Map<string, Checkpoint>; | ||
| listeners: Set<Listener>; | ||
| } | ||
|
|
||
| const TTID = 'ttid'; | ||
| const TTFD = 'ttfd'; | ||
|
|
||
| export type DisplayKind = typeof TTID | typeof TTFD; | ||
|
|
||
| const registries: Record<DisplayKind, Map<string, SpanRegistry>> = { | ||
| ttid: new Map(), | ||
| ttfd: new Map(), | ||
| }; | ||
|
|
||
| function getOrCreate(kind: DisplayKind, parentSpanId: string): SpanRegistry { | ||
| const map = registries[kind]; | ||
| let entry = map.get(parentSpanId); | ||
| if (!entry) { | ||
| entry = { | ||
| checkpoints: new Map(), | ||
| listeners: new Set() | ||
| }; | ||
| map.set(parentSpanId, entry); | ||
| } | ||
| return entry; | ||
| } | ||
|
|
||
| function performCleanup(kind: DisplayKind, parentSpanId: string, entry: SpanRegistry): void { | ||
| if (entry.checkpoints.size === 0 && entry.listeners.size === 0) { | ||
| registries[kind].delete(parentSpanId); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Register a checkpoint under (kind, parentSpanId). Returns an unregister fn. | ||
| */ | ||
| export function registerCheckpoint( | ||
| kind: DisplayKind, | ||
| parentSpanId: string, | ||
| checkpointId: string, | ||
| ready: boolean, | ||
| ): () => void { | ||
| const entry = getOrCreate(kind, parentSpanId); | ||
| entry.checkpoints.set(checkpointId, { ready }); | ||
| notify(entry); | ||
|
|
||
| return () => { | ||
| const e = registries[kind].get(parentSpanId); | ||
| if (!e) { | ||
| return; | ||
| } | ||
| if (e.checkpoints.delete(checkpointId)) { | ||
| notify(e); | ||
| } | ||
| performCleanup(kind, parentSpanId, e); | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Update an existing checkpoint's ready state. | ||
| */ | ||
| export function updateCheckpoint( | ||
| kind: DisplayKind, | ||
| parentSpanId: string, | ||
| checkpointId: string, | ||
| ready: boolean, | ||
| ): void { | ||
| const entry = registries[kind].get(parentSpanId); | ||
| const cp = entry?.checkpoints.get(checkpointId); | ||
| if (!entry || !cp || cp.ready === ready) { | ||
| return; | ||
| } | ||
| cp.ready = ready; | ||
| notify(entry); | ||
| } | ||
|
|
||
| /** | ||
| * True if at least one checkpoint is registered AND all checkpoints are ready. | ||
| */ | ||
| export function isAllReady(kind: DisplayKind, parentSpanId: string): boolean { | ||
| const entry = registries[kind].get(parentSpanId); | ||
| if (!entry || entry.checkpoints.size === 0) { | ||
| return false; | ||
| } | ||
| for (const cp of entry.checkpoints.values()) { | ||
| if (!cp.ready) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Returns true if there is at least one registered checkpoint on this span. | ||
| */ | ||
| export function hasAnyCheckpoints(kind: DisplayKind, parentSpanId: string): boolean { | ||
| const entry = registries[kind].get(parentSpanId); | ||
| return !!entry && entry.checkpoints.size > 0; | ||
| } | ||
|
|
||
| /** | ||
| * Subscribe to any checkpoint state change for a given span. The listener is | ||
| * called synchronously after each register/update/unregister event. | ||
| */ | ||
| export function subscribe(kind: DisplayKind, parentSpanId: string, listener: Listener): () => void { | ||
| const entry = getOrCreate(kind, parentSpanId); | ||
| entry.listeners.add(listener); | ||
| return () => { | ||
| const e = registries[kind].get(parentSpanId); | ||
| if (!e) { | ||
| return; | ||
| } | ||
| e.listeners.delete(listener); | ||
| performCleanup(kind, parentSpanId, e); | ||
| }; | ||
| } | ||
|
|
||
| function notify(entry: SpanRegistry): void { | ||
| for (const listener of entry.listeners) { | ||
| listener(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Test-only. Clears all coordinator state. | ||
| */ | ||
| export function _resetTimeToDisplayCoordinator(): void { | ||
| registries.ttid.clear(); | ||
| registries.ttfd.clear(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.