Commit a844274
Bound the display-phase event beat induce to one per run loop turn (#58642)
Summary:
The beat an Apple display phase induces runs the whole event loop tick on the
calling thread, mounting included. A view whose layout metrics change during
that mount can emit another synchronous request from inside the display that is
servicing the first one, and Core Animation honours a `setNeedsDisplay` made
during a display by running the commit's layout and display phases again, with
no bound. One Core Animation commit can therefore perform an unbounded number of
blocking JavaScript round trips.
`AppleEventBeat` now induces at most once per run loop turn from the display
phase. The run loop observer, which runs before Core Animation's commit
observer, opens each turn. Requests arriving after that first induce keep the
ordinary observer timing — what they had before the display-phase induce
existed — so this bounds the tail without giving up the guarantee the
display-phase induce was added for: the first layout-driven request of a frame
is still processed in that frame.
Measured on an iPhone 11 / iOS 26.0 simulator with a virtualized list whose
placeholder is taller than its content, so making one row visible pulls siblings
into the viewport. Identical scenario, 13 synchronous requests in every arm:
| arm | worst single main-thread block | induces in one commit |
| --- | --- | --- |
| display-phase induce, unbounded | 145.8 ms | 11 |
| no display-phase induce | 15.5 ms | n/a |
| display-phase induce, bounded (this change) | 14.9 ms | 1 |
A separate probe established that Core Animation itself imposes no bound: a
zero-sized layer that re-dirties itself from inside its own `display` ran 200
display passes in one commit, stopped only by the probe's own cap.
Changelog:
[iOS][Fixed] - Process at most one synchronous event beat per Core Animation commit, so a mount performed during the display phase cannot re-enter it without bound
Differential Revision: D1209515921 parent 18f5ddb commit a844274
2 files changed
Lines changed: 21 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
73 | 87 | | |
74 | 88 | | |
75 | 89 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
72 | 76 | | |
73 | 77 | | |
74 | 78 | | |
| |||
117 | 121 | | |
118 | 122 | | |
119 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
120 | 127 | | |
121 | 128 | | |
122 | 129 | | |
| |||
0 commit comments