Bug 1457590 - Add the HitTestingTreeNodeAutoLock class. r=botond

This adds the HitTestingTreeNodeAutoLock RAII class that allows using
a HitTestingTreeNode safely outside a tree lock, and ensures that the
node won't get destroyed or recycled concurrently.

MozReview-Commit-ID: 8Tb3vdIeUgr

--HG--
extra : rebase_source : 6eed3e733edcaa4088da52882a9b3dc8c2355c2e
This commit is contained in:
Kartikaya Gupta 2018-05-30 15:54:50 -04:00
parent 46df7cfee3
commit 6acdee2bb0
2 changed files with 96 additions and 1 deletions

View File

@ -24,6 +24,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
LayersId aLayersId)
: mApzc(aApzc)
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
, mLocked(false)
, mLayersId(aLayersId)
, mScrollbarAnimationId(0)
, mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
@ -73,7 +74,7 @@ HitTestingTreeNode::Destroy()
bool
HitTestingTreeNode::IsRecyclable(const RecursiveMutexAutoLock& aProofOfTreeLock)
{
return !IsPrimaryHolder();
return !(IsPrimaryHolder() || mLocked);
}
void
@ -406,5 +407,58 @@ HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent)
}
}
void
HitTestingTreeNode::Lock(const RecursiveMutexAutoLock& aProofOfTreeLock)
{
MOZ_ASSERT(!mLocked);
mLocked = true;
}
void
HitTestingTreeNode::Unlock(const RecursiveMutexAutoLock& aProofOfTreeLock)
{
MOZ_ASSERT(mLocked);
mLocked = false;
}
HitTestingTreeNodeAutoLock::HitTestingTreeNodeAutoLock()
: mTreeMutex(nullptr)
{
}
HitTestingTreeNodeAutoLock::~HitTestingTreeNodeAutoLock()
{
Clear();
}
void
HitTestingTreeNodeAutoLock::Initialize(const RecursiveMutexAutoLock& aProofOfTreeLock,
already_AddRefed<HitTestingTreeNode> aNode,
RecursiveMutex& aTreeMutex)
{
MOZ_ASSERT(!mNode);
mNode = aNode;
mTreeMutex = &aTreeMutex;
mNode->Lock(aProofOfTreeLock);
}
void
HitTestingTreeNodeAutoLock::Clear()
{
if (!mNode) {
return;
}
MOZ_ASSERT(mTreeMutex);
{ // scope lock
RecursiveMutexAutoLock lock(*mTreeMutex);
mNode->Unlock(lock);
}
mNode = nullptr;
mTreeMutex = nullptr;
}
} // namespace layers
} // namespace mozilla

View File

@ -48,6 +48,13 @@ class AsyncPanZoomController;
* Accessing the compositor layer tree can only be done on the compositor
* thread, and so it is simpler to make a copy of the hit-testing related
* properties into a separate tree.
*
* The tree pointers on the node (mLastChild, etc.) can only be manipulated
* while holding the APZ tree lock. Any code that wishes to use a
* HitTestingTreeNode outside of holding the tree lock should do so by using
* the HitTestingTreeNodeAutoLock wrapper, which prevents the node from
* being recycled (and also holds a RefPtr to the node to prevent it from
* getting freed).
*/
class HitTestingTreeNode {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
@ -137,6 +144,12 @@ public:
void Dump(const char* aPrefix = "") const;
private:
friend class HitTestingTreeNodeAutoLock;
// Functions that are private but called from HitTestingTreeNodeAutoLock
void Lock(const RecursiveMutexAutoLock& aProofOfTreeLock);
void Unlock(const RecursiveMutexAutoLock& aProofOfTreeLock);
void SetApzcParent(AsyncPanZoomController* aApzc);
RefPtr<HitTestingTreeNode> mLastChild;
@ -145,6 +158,7 @@ private:
RefPtr<AsyncPanZoomController> mApzc;
bool mIsPrimaryApzcHolder;
bool mLocked;
LayersId mLayersId;
@ -193,6 +207,33 @@ private:
EventRegionsOverride mOverride;
};
/**
* A class that allows safe usage of a HitTestingTreeNode outside of the APZ
* tree lock. In general, this class should be Initialize()'d inside the tree
* lock (enforced by the proof-of-lock to Initialize), and then can be returned
* to a scope outside the tree lock and used safely. Upon destruction or
* Clear() being called, it unlocks the underlying node at which point it can
* be recycled or freed.
*/
class MOZ_RAII HitTestingTreeNodeAutoLock
{
public:
HitTestingTreeNodeAutoLock();
HitTestingTreeNodeAutoLock(const HitTestingTreeNodeAutoLock&) = delete;
HitTestingTreeNodeAutoLock& operator=(const HitTestingTreeNodeAutoLock&) = delete;
HitTestingTreeNodeAutoLock(HitTestingTreeNodeAutoLock&&) = delete;
~HitTestingTreeNodeAutoLock();
void Initialize(const RecursiveMutexAutoLock& aProofOfTreeLock,
already_AddRefed<HitTestingTreeNode> aNode,
RecursiveMutex& aTreeMutex);
void Clear();
private:
RefPtr<HitTestingTreeNode> mNode;
RecursiveMutex* mTreeMutex;
};
} // namespace layers
} // namespace mozilla