Skip to content

Commit 0d9a8c7

Browse files
committed
AX: Accessing AXIsolatedTree::{m_relations, m_selectedTextMarkerRange} requires m_changeLogLock, which can cause the AX thread to block on the main-thread or vice versa
https://bugs.webkit.org/show_bug.cgi?id=294652 rdar://153703482 Reviewed by Joshua Hoffman. Ideally, the accessibility thread should only synchronize with the main-thread via AXIsolatedTree::m_changeLogLock once: at the beginning of serving a request, via AXIsolatedTree::applyPendingChanges(). But prior to this commit, we would take the lock any time we needed to access AXIsolatedTree::m_relations or AXIsolatedTree::m_selectedTextMarkerRange, which could potentially be a lot. This can cause the AX thread to block on the main-thread at random times, and vice versa, which can harm performance. With this commit, we move caching the selected text marker range and relations map to a similar approach taken for other synced data structures. We create an m_pendingFoo equivalent, guarded by a lock, and apply that in AXIsolatedTree::applyPendingChanges. Then we can use the synced data structure safely without a lock off the main-thread. * Source/WebCore/accessibility/AXObjectCache.cpp: (WebCore::AXObjectCache::dirtyIsolatedTreeRelations): * Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp: (WebCore::AXIsolatedTree::create): (WebCore::AXIsolatedTree::updateRelations): (WebCore::AXIsolatedTree::setSelectedTextMarkerRange): (WebCore::AXIsolatedTree::relatedObjectIDsFor): (WebCore::AXIsolatedTree::applyPendingChanges): (WebCore::AXIsolatedTree::selectedTextMarkerRange): Deleted. * Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h: (WebCore::AXIsolatedTree::markRelationsDirty): (WebCore::AXIsolatedTree::selectedTextMarkerRange): (WebCore::AXIsolatedTree::relationsNeedUpdate): Deleted. Canonical link: https://commits.webkit.org/296378@main
1 parent 10ba492 commit 0d9a8c7

3 files changed

Lines changed: 20 additions & 24 deletions

File tree

Source/WebCore/accessibility/AXObjectCache.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,8 +3290,8 @@ void AXObjectCache::dirtyIsolatedTreeRelations()
32903290
AXTRACE("AXObjectCache::dirtyIsolatedTreeRelations"_s);
32913291

32923292
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
3293-
if (auto tree = AXIsolatedTree::treeForPageID(m_pageID))
3294-
tree->relationsNeedUpdate(true);
3293+
if (RefPtr tree = AXIsolatedTree::treeForPageID(m_pageID))
3294+
tree->markRelationsDirty();
32953295
startUpdateTreeSnapshotTimer();
32963296
#endif
32973297
}

Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,13 @@ RefPtr<AXIsolatedTree> AXIsolatedTree::create(AXObjectCache& axObjectCache)
168168
tree->setInitialSortedNonRootWebAreas(axIDs(axObjectCache.sortedNonRootWebAreas()));
169169
tree->updateLoadingProgress(axObjectCache.loadingProgress());
170170

171-
const auto relations = axObjectCache.relations();
172-
tree->updateRelations(relations);
173-
171+
auto relations = axObjectCache.relations();
174172
for (auto& relatedObjectID : relations.keys()) {
175173
RefPtr axObject = axObjectCache.objectForID(relatedObjectID);
176174
if (axObject && axObject->isIgnored())
177175
tree->addUnconnectedNode(axObject.releaseNonNull());
178176
}
177+
tree->updateRelations(WTFMove(relations));
179178

180179
// Now that the tree is ready to take client requests, add it to the tree maps so that it can be found.
181180
storeTree(axObjectCache, tree);
@@ -1172,21 +1171,14 @@ void AXIsolatedTree::setFocusedNodeID(std::optional<AXID> axID)
11721171
m_pendingFocusedNodeID = axID;
11731172
}
11741173

1175-
void AXIsolatedTree::updateRelations(const HashMap<AXID, AXRelations>& relations)
1174+
void AXIsolatedTree::updateRelations(HashMap<AXID, AXRelations>&& relations)
11761175
{
11771176
AXTRACE("AXIsolatedTree::updateRelations"_s);
11781177
ASSERT(isMainThread());
11791178

1180-
Locker locker { m_changeLogLock };
1181-
m_relations = relations;
11821179
m_relationsNeedUpdate = false;
1183-
}
1184-
1185-
AXTextMarkerRange AXIsolatedTree::selectedTextMarkerRange()
1186-
{
1187-
AXTRACE("AXIsolatedTree::selectedTextMarkerRange"_s);
11881180
Locker locker { m_changeLogLock };
1189-
return m_selectedTextMarkerRange;
1181+
m_pendingRelations = WTFMove(relations);
11901182
}
11911183

11921184
void AXIsolatedTree::setSelectedTextMarkerRange(AXTextMarkerRange&& range)
@@ -1195,7 +1187,7 @@ void AXIsolatedTree::setSelectedTextMarkerRange(AXTextMarkerRange&& range)
11951187
ASSERT(isMainThread());
11961188

11971189
Locker locker { m_changeLogLock };
1198-
m_selectedTextMarkerRange = range;
1190+
m_pendingSelectedTextMarkerRange = WTFMove(range);
11991191
}
12001192

12011193
void AXIsolatedTree::updateLoadingProgress(double newProgressValue)
@@ -1286,8 +1278,6 @@ void AXIsolatedTree::removeSubtreeFromNodeMap(std::optional<AXID> objectID, std:
12861278
std::optional<ListHashSet<AXID>> AXIsolatedTree::relatedObjectIDsFor(const AXIsolatedObject& object, AXRelation relation)
12871279
{
12881280
ASSERT(!isMainThread());
1289-
// FIXME: Taking the lock any time we want to access relations isn't ideal for performance.
1290-
Locker locker { m_changeLogLock };
12911281

12921282
auto relationsIterator = m_relations.find(object.objectID());
12931283
if (relationsIterator == m_relations.end())
@@ -1413,6 +1403,12 @@ void AXIsolatedTree::applyPendingChanges()
14131403
if (m_pendingMostRecentlyPaintedText)
14141404
m_mostRecentlyPaintedText = std::exchange(m_pendingMostRecentlyPaintedText, std::nullopt).value();
14151405

1406+
if (m_pendingRelations)
1407+
m_relations = std::exchange(m_pendingRelations, std::nullopt).value();
1408+
1409+
if (m_pendingSelectedTextMarkerRange)
1410+
m_selectedTextMarkerRange = std::exchange(m_pendingSelectedTextMarkerRange, std::nullopt).value();
1411+
14161412
// Do this at the end because it requires looking up the root node by ID, so doing it at the end
14171413
// ensures all additions to m_readerThreadNodeMap have been made by now.
14181414
applyPendingRootNodeLocked();

Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
498498

499499
// Relationships between objects.
500500
std::optional<ListHashSet<AXID>> relatedObjectIDsFor(const AXIsolatedObject&, AXRelation);
501-
void relationsNeedUpdate(bool needUpdate) { m_relationsNeedUpdate = needUpdate; }
502-
void updateRelations(const HashMap<AXID, AXRelations>&);
501+
void markRelationsDirty() { m_relationsNeedUpdate = true; }
502+
void updateRelations(HashMap<AXID, AXRelations>&&);
503503

504504
AXCoreObject::AccessibilityChildrenVector sortedLiveRegions();
505505
AXCoreObject::AccessibilityChildrenVector sortedNonRootWebAreas();
@@ -517,7 +517,7 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
517517
// Use only if the s_storeLock is already held like in findAXTree.
518518
WEBCORE_EXPORT OptionSet<ActivityState> lockedPageActivityState() const;
519519

520-
AXTextMarkerRange selectedTextMarkerRange();
520+
AXTextMarkerRange selectedTextMarkerRange() { return m_selectedTextMarkerRange; }
521521
void setSelectedTextMarkerRange(AXTextMarkerRange&&);
522522

523523
void sortedLiveRegionsDidChange(Vector<AXID>);
@@ -641,6 +641,8 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
641641
std::optional<Vector<AXID>> m_pendingSortedLiveRegionIDs WTF_GUARDED_BY_LOCK(m_changeLogLock);
642642
std::optional<Vector<AXID>> m_pendingSortedNonRootWebAreaIDs WTF_GUARDED_BY_LOCK(m_changeLogLock);
643643
std::optional<HashMap<AXID, LineRange>> m_pendingMostRecentlyPaintedText WTF_GUARDED_BY_LOCK(m_changeLogLock);
644+
std::optional<HashMap<AXID, AXRelations>> m_pendingRelations WTF_GUARDED_BY_LOCK(m_changeLogLock);
645+
std::optional<AXTextMarkerRange> m_pendingSelectedTextMarkerRange WTF_GUARDED_BY_LOCK(m_changeLogLock);
644646
Markable<AXID> m_focusedNodeID;
645647
std::atomic<double> m_loadingProgress { 0 };
646648
std::atomic<double> m_processingProgress { 1 };
@@ -649,17 +651,15 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
649651
Vector<AXID> m_sortedLiveRegionIDs;
650652
Vector<AXID> m_sortedNonRootWebAreaIDs;
651653
HashMap<AXID, LineRange> m_mostRecentlyPaintedText;
652-
653-
// Relationships between objects.
654-
HashMap<AXID, AXRelations> m_relations WTF_GUARDED_BY_LOCK(m_changeLogLock);
654+
HashMap<AXID, AXRelations> m_relations;
655655

656656
// Set to true by the AXObjectCache and false by AXIsolatedTree.
657657
// Both are only to be used on the main-thread.
658658
bool m_relationsNeedUpdate { true };
659659
bool m_mostRecentlyPaintedTextIsDirty { true };
660660

661661
Lock m_changeLogLock;
662-
AXTextMarkerRange m_selectedTextMarkerRange WTF_GUARDED_BY_LOCK(m_changeLogLock);
662+
AXTextMarkerRange m_selectedTextMarkerRange;
663663

664664
// Queued node updates used for building a new tree snapshot.
665665
ListHashSet<AXID> m_needsUpdateChildren;

0 commit comments

Comments
 (0)