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

Reply via email to