Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 680e227dc899bb9e77fccdaa294eb7bb506d07de
https://github.com/WebKit/WebKit/commit/680e227dc899bb9e77fccdaa294eb7bb506d07de
Author: Tyler Wilcock <[email protected]>
Date: 2026-05-06 (Wed, 06 May 2026)
Changed paths:
M LayoutTests/accessibility/mac/client/hierarchical-level.html
M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp
M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h
Log Message:
-----------
AX: Reduce AXIsolatedTree::applyPendingChanges lock contention and skip work
when no changes are pending
https://bugs.webkit.org/show_bug.cgi?id=313751
rdar://175648233
Reviewed by Dominic Mazzoni.
applyPendingChanges() can be called extremely frequently, at least once per
request, and potentially a lot
more via crossFrameChildObject(), which ensures the local frame accessibility
tree is up-to-date. Based
on logging I added, ~98% of the time there are no pending changes to be
applied, yet we do a bunch of
work anyways — set up signposts, checks tons of data structures, allocate a
std::function of decent size.
Additionally, when there are changes to apply, it can take a decent amount of
time to do so (as high as 22ms
from my measurement). We hold the m_changeLogLock this whole time, which caused
frequent main-thread stalls
waiting for the lock (~300ms total over the course of 120 seconds of VO-Right
on a fairly static webpage -- dynamic
webpages could be even worse).
This commit implements three optimizations:
1. Add std::atomic<bool> m_hasPendingChanges flag checked before acquiring
the lock,
allowing the common no-op case to return without any locking. Uses relaxed
memory
ordering since the lock provides the real synchronization, the flag is
just a hint.
2. Snapshot-and-release: move all pending data into a local
PendingChangesSnapshot under
the lock, release the lock, then process the snapshot. This reduces
main-thread lock
contention from the full processing duration to ~15 pointer swaps.
3. Convert the recursive deleteSubtree from a std::function lambda (which
heap-allocated
captures including Ref<AXIsolatedTree>) into a private member function,
eliminating
per-call allocation and refcount overhead.
Using TimingScope, I measured a before and after from ~120 seconds of VO-Right
on a fairly static webpage:
BEFORE: applyPendingChanges(): 999660 calls, mean duration: 0.006174ms, total
duration: 6172.396803ms, max duration 22.998125ms
AFTER: applyPendingChanges(): 999660 calls, mean duration: 0.000313ms, total
duration: 312.858223ms, max duration 13.276667ms
And the main-thread went from frequent 5-20ms stalls waiting for the lock to
significantly less frequent stalls that
wait 1ms at most.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::queueForDestruction):
(WebCore::AXIsolatedTree::createEmptyContent):
(WebCore::AXIsolatedTree::queueChange):
(WebCore::AXIsolatedTree::addUnconnectedNode):
(WebCore::AXIsolatedTree::queueRemovals):
(WebCore::AXIsolatedTree::queueRemovalsLocked):
(WebCore::AXIsolatedTree::updateNodeProperties):
(WebCore::AXIsolatedTree::overrideNodeProperties):
(WebCore::AXIsolatedTree::setPendingRootNodeIDLocked):
(WebCore::AXIsolatedTree::setFocusedNodeID):
(WebCore::AXIsolatedTree::updateRelations):
(WebCore::AXIsolatedTree::setSelectedTextMarkerRange):
(WebCore::AXIsolatedTree::setFrameGeometry):
(WebCore::AXIsolatedTree::updateFrame):
(WebCore::AXIsolatedTree::applyPendingChanges):
(WebCore::AXIsolatedTree::applyPendingChangesUnlessQueuedForDestruction):
(WebCore::AXIsolatedTree::applyPendingChangesOrTearDown):
(WebCore::AXIsolatedTree::removeStaleAppends):
(WebCore::AXIsolatedTree::deleteSubtree):
(WebCore::AXIsolatedTree::takePendingChangesLocked):
(WebCore::AXIsolatedTree::applyPendingChangesFromSnapshot):
(WebCore::AXIsolatedTree::sortedLiveRegionsDidChange):
(WebCore::AXIsolatedTree::sortedNonRootWebAreasDidChange):
(WebCore::AXIsolatedTree::processQueuedNodeUpdates):
(WebCore::AXIsolatedTree::applyPendingRootNodeLocked): Deleted.
(WebCore::AXIsolatedTree::applyPendingChangesLocked): Deleted.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
(WebCore::AXIsolatedTree::setHasPendingChanges):
(WebCore::AXIsolatedTree::hasPendingChanges const):
Canonical link: https://commits.webkit.org/312765@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications