xref: /aosp_15_r20/external/angle/src/libANGLE/ContextMutex.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker // ContextMutex.h: Classes for protecting Context access and EGLImage siblings.
7*8975f5c5SAndroid Build Coastguard Worker 
8*8975f5c5SAndroid Build Coastguard Worker #ifndef LIBANGLE_CONTEXT_MUTEX_H_
9*8975f5c5SAndroid Build Coastguard Worker #define LIBANGLE_CONTEXT_MUTEX_H_
10*8975f5c5SAndroid Build Coastguard Worker 
11*8975f5c5SAndroid Build Coastguard Worker #include <atomic>
12*8975f5c5SAndroid Build Coastguard Worker 
13*8975f5c5SAndroid Build Coastguard Worker #include "common/debug.h"
14*8975f5c5SAndroid Build Coastguard Worker 
15*8975f5c5SAndroid Build Coastguard Worker namespace gl
16*8975f5c5SAndroid Build Coastguard Worker {
17*8975f5c5SAndroid Build Coastguard Worker class Context;
18*8975f5c5SAndroid Build Coastguard Worker }
19*8975f5c5SAndroid Build Coastguard Worker 
20*8975f5c5SAndroid Build Coastguard Worker namespace egl
21*8975f5c5SAndroid Build Coastguard Worker {
22*8975f5c5SAndroid Build Coastguard Worker #if defined(ANGLE_ENABLE_CONTEXT_MUTEX)
23*8975f5c5SAndroid Build Coastguard Worker constexpr bool kIsContextMutexEnabled = true;
24*8975f5c5SAndroid Build Coastguard Worker #else
25*8975f5c5SAndroid Build Coastguard Worker constexpr bool kIsContextMutexEnabled = false;
26*8975f5c5SAndroid Build Coastguard Worker #endif
27*8975f5c5SAndroid Build Coastguard Worker 
28*8975f5c5SAndroid Build Coastguard Worker // Use standard mutex for now
29*8975f5c5SAndroid Build Coastguard Worker using ContextMutexType = std::mutex;
30*8975f5c5SAndroid Build Coastguard Worker 
31*8975f5c5SAndroid Build Coastguard Worker class ContextMutex final : angle::NonCopyable
32*8975f5c5SAndroid Build Coastguard Worker {
33*8975f5c5SAndroid Build Coastguard Worker   public:
34*8975f5c5SAndroid Build Coastguard Worker     explicit ContextMutex(ContextMutex *root = nullptr);
35*8975f5c5SAndroid Build Coastguard Worker     // For "leaf" mutex its "root" must be locked during destructor call.
36*8975f5c5SAndroid Build Coastguard Worker     ~ContextMutex();
37*8975f5c5SAndroid Build Coastguard Worker 
38*8975f5c5SAndroid Build Coastguard Worker     // Merges mutexes so they work as one.
39*8975f5c5SAndroid Build Coastguard Worker     // At the end, only single "root" mutex will be locked.
40*8975f5c5SAndroid Build Coastguard Worker     // Does nothing if two mutexes are the same or already merged (have same "root" mutex).
41*8975f5c5SAndroid Build Coastguard Worker     static void Merge(ContextMutex *lockedMutex, ContextMutex *otherMutex);
42*8975f5c5SAndroid Build Coastguard Worker 
43*8975f5c5SAndroid Build Coastguard Worker     // Returns current "root" mutex.
44*8975f5c5SAndroid Build Coastguard Worker     // Warning! Result is only stable if mutex is locked, while may change any time if unlocked.
45*8975f5c5SAndroid Build Coastguard Worker     // May be used to compare against already locked "root" mutex.
getRoot()46*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ContextMutex *getRoot() { return mRoot.load(std::memory_order_relaxed); }
getRoot()47*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE const ContextMutex *getRoot() const
48*8975f5c5SAndroid Build Coastguard Worker     {
49*8975f5c5SAndroid Build Coastguard Worker         return mRoot.load(std::memory_order_relaxed);
50*8975f5c5SAndroid Build Coastguard Worker     }
51*8975f5c5SAndroid Build Coastguard Worker 
52*8975f5c5SAndroid Build Coastguard Worker     // Below group of methods are not thread safe and must be protected by "this" mutex instance.
addRef()53*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE void addRef() { ++mRefCount; }
release()54*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE void release() { release(UnlockBehaviour::kDoNotUnlock); }
55*8975f5c5SAndroid Build Coastguard Worker     // Must be only called on "root" mutex.
releaseAndUnlock()56*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE void releaseAndUnlock() { release(UnlockBehaviour::kUnlock); }
isReferenced()57*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE bool isReferenced() const { return mRefCount > 0; }
58*8975f5c5SAndroid Build Coastguard Worker 
59*8975f5c5SAndroid Build Coastguard Worker     bool try_lock();
60*8975f5c5SAndroid Build Coastguard Worker     void lock();
61*8975f5c5SAndroid Build Coastguard Worker     void unlock();
62*8975f5c5SAndroid Build Coastguard Worker 
63*8975f5c5SAndroid Build Coastguard Worker   private:
64*8975f5c5SAndroid Build Coastguard Worker     enum class UnlockBehaviour
65*8975f5c5SAndroid Build Coastguard Worker     {
66*8975f5c5SAndroid Build Coastguard Worker         kDoNotUnlock,
67*8975f5c5SAndroid Build Coastguard Worker         kUnlock
68*8975f5c5SAndroid Build Coastguard Worker     };
69*8975f5c5SAndroid Build Coastguard Worker 
70*8975f5c5SAndroid Build Coastguard Worker     bool tryLockImpl();
71*8975f5c5SAndroid Build Coastguard Worker     void lockImpl();
72*8975f5c5SAndroid Build Coastguard Worker     void unlockImpl();
73*8975f5c5SAndroid Build Coastguard Worker 
74*8975f5c5SAndroid Build Coastguard Worker     // All methods below must be protected by "this" mutex ("stable root" in "this" instance).
75*8975f5c5SAndroid Build Coastguard Worker 
76*8975f5c5SAndroid Build Coastguard Worker     void setNewRoot(ContextMutex *newRoot);
77*8975f5c5SAndroid Build Coastguard Worker     void addLeaf(ContextMutex *leaf);
78*8975f5c5SAndroid Build Coastguard Worker     void removeLeaf(ContextMutex *leaf);
79*8975f5c5SAndroid Build Coastguard Worker 
80*8975f5c5SAndroid Build Coastguard Worker     void release(UnlockBehaviour unlockBehaviour);
81*8975f5c5SAndroid Build Coastguard Worker 
82*8975f5c5SAndroid Build Coastguard Worker   private:
83*8975f5c5SAndroid Build Coastguard Worker     // mRoot and mLeaves tree structure details:
84*8975f5c5SAndroid Build Coastguard Worker     // - used to implement primary functionality of this class;
85*8975f5c5SAndroid Build Coastguard Worker     // - initially, all mutexes are "root"s;
86*8975f5c5SAndroid Build Coastguard Worker     // - "root" mutex has "mRoot == this";
87*8975f5c5SAndroid Build Coastguard Worker     // - "root" mutex stores unreferenced pointers to all its leaves (used in merging);
88*8975f5c5SAndroid Build Coastguard Worker     // - "leaf" mutex holds reference (addRef) to the current "root" mutex in the mRoot;
89*8975f5c5SAndroid Build Coastguard Worker     // - "leaf" mutex has empty mLeaves;
90*8975f5c5SAndroid Build Coastguard Worker     // - "leaf" mutex can't become a "root" mutex;
91*8975f5c5SAndroid Build Coastguard Worker     // - before locking the mMutex, "this" is an "unstable root" or a "leaf";
92*8975f5c5SAndroid Build Coastguard Worker     // - the implementation always locks mRoot's mMutex ("unstable root");
93*8975f5c5SAndroid Build Coastguard Worker     // - if after locking the mMutex "mRoot != this", then "this" is/become a "leaf";
94*8975f5c5SAndroid Build Coastguard Worker     // - otherwise, "this" is a locked "stable root" - lock is successful.
95*8975f5c5SAndroid Build Coastguard Worker 
96*8975f5c5SAndroid Build Coastguard Worker     // mOldRoots is used to solve a particular problem (below example does not use mRank):
97*8975f5c5SAndroid Build Coastguard Worker     // - have "leaf" mutex_2 with a reference to mutex_1 "root";
98*8975f5c5SAndroid Build Coastguard Worker     // - the mutex_1 has no other references (only in the mutex_2);
99*8975f5c5SAndroid Build Coastguard Worker     // - have other mutex_3 "root";
100*8975f5c5SAndroid Build Coastguard Worker     // - mutex_1 pointer is cached on the stack during locking of mutex_2 (thread A);
101*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_3 and mutex_2 (thread B):
102*8975f5c5SAndroid Build Coastguard Worker     //     * now "leaf" mutex_2 stores reference to mutex_3 "root";
103*8975f5c5SAndroid Build Coastguard Worker     //     * old "root" mutex_1 becomes a "leaf" of mutex_3;
104*8975f5c5SAndroid Build Coastguard Worker     //     * old "root" mutex_1 has no references and gets destroyed.
105*8975f5c5SAndroid Build Coastguard Worker     // - invalid pointer to destroyed mutex_1 stored on the stack and in the mLeaves of mutex_3;
106*8975f5c5SAndroid Build Coastguard Worker     // - to fix this problem, references to old "root"s are kept in the mOldRoots vector.
107*8975f5c5SAndroid Build Coastguard Worker 
108*8975f5c5SAndroid Build Coastguard Worker     // mRank is used to fix a problem of indefinite grows of mOldRoots:
109*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_2 and mutex_1 -> mutex_2 is "root" of mutex_1 (mOldRoots == 0);
110*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_2;
111*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_3 and mutex_1 -> mutex_3 is "root" of mutex_1 (mOldRoots == 1);
112*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_3;
113*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_4 and mutex_1 -> mutex_4 is "root" of mutex_1 (mOldRoots == 2);
114*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_4;
115*8975f5c5SAndroid Build Coastguard Worker     // - continuing this pattern can lead to indefinite grows of mOldRoots, while pick number of
116*8975f5c5SAndroid Build Coastguard Worker     //   mutexes is only 2.
117*8975f5c5SAndroid Build Coastguard Worker     // Fix details using mRank:
118*8975f5c5SAndroid Build Coastguard Worker     // - initially "mRank == 0" and only relevant for "root" mutexes;
119*8975f5c5SAndroid Build Coastguard Worker     // - merging mutexes with equal mRank of their "root"s, will use first (lockedMutex) "root"
120*8975f5c5SAndroid Build Coastguard Worker     //   mutex as a new "root" and increase its mRank by 1;
121*8975f5c5SAndroid Build Coastguard Worker     // - otherwise, "root" mutex with a highest rank will be used without changing the mRank;
122*8975f5c5SAndroid Build Coastguard Worker     // - this way, "stronger" (with a higher mRank) "root" mutex will "protect" its "leaves" from
123*8975f5c5SAndroid Build Coastguard Worker     //   "mRoot" replacement and therefore - mOldRoots grows.
124*8975f5c5SAndroid Build Coastguard Worker     // Lets look at the problematic pattern with the mRank:
125*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_2 and mutex_1 -> mutex_2 is "root" (mRank == 1) of mutex_1 (mOldRoots == 0);
126*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_2;
127*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_3 and mutex_1 -> mutex_2 is "root" (mRank == 1) of mutex_3 (mOldRoots == 0);
128*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_3;
129*8975f5c5SAndroid Build Coastguard Worker     // - merge mutex_4 and mutex_1 -> mutex_2 is "root" (mRank == 1) of mutex_4 (mOldRoots == 0);
130*8975f5c5SAndroid Build Coastguard Worker     // - destroy mutex_4;
131*8975f5c5SAndroid Build Coastguard Worker     // - no mOldRoots grows at all;
132*8975f5c5SAndroid Build Coastguard Worker     // - minumum number of mutexes to reach mOldRoots size of N => 2^(N+1).
133*8975f5c5SAndroid Build Coastguard Worker 
134*8975f5c5SAndroid Build Coastguard Worker     std::atomic<ContextMutex *> mRoot;
135*8975f5c5SAndroid Build Coastguard Worker     ContextMutexType mMutex;
136*8975f5c5SAndroid Build Coastguard Worker     // Used when ASSERT() and/or recursion are/is enabled.
137*8975f5c5SAndroid Build Coastguard Worker     std::atomic<angle::ThreadId> mOwnerThreadId;
138*8975f5c5SAndroid Build Coastguard Worker     // Used only when recursion is enabled.
139*8975f5c5SAndroid Build Coastguard Worker     uint32_t mLockLevel;
140*8975f5c5SAndroid Build Coastguard Worker     size_t mRefCount;
141*8975f5c5SAndroid Build Coastguard Worker 
142*8975f5c5SAndroid Build Coastguard Worker     std::set<ContextMutex *> mLeaves;
143*8975f5c5SAndroid Build Coastguard Worker     std::vector<ContextMutex *> mOldRoots;
144*8975f5c5SAndroid Build Coastguard Worker     uint32_t mRank;
145*8975f5c5SAndroid Build Coastguard Worker };
146*8975f5c5SAndroid Build Coastguard Worker 
147*8975f5c5SAndroid Build Coastguard Worker // Prevents destruction while locked, uses mMutex to protect addRef()/releaseAndUnlock() calls.
148*8975f5c5SAndroid Build Coastguard Worker class [[nodiscard]] ScopedContextMutexAddRefLock final : angle::NonCopyable
149*8975f5c5SAndroid Build Coastguard Worker {
150*8975f5c5SAndroid Build Coastguard Worker   public:
151*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexAddRefLock() = default;
ScopedContextMutexAddRefLock(ContextMutex & mutex)152*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE explicit ScopedContextMutexAddRefLock(ContextMutex &mutex) { lock(&mutex); }
ScopedContextMutexAddRefLock(ContextMutex * mutex)153*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexAddRefLock(ContextMutex *mutex)
154*8975f5c5SAndroid Build Coastguard Worker     {
155*8975f5c5SAndroid Build Coastguard Worker         if (mutex != nullptr)
156*8975f5c5SAndroid Build Coastguard Worker         {
157*8975f5c5SAndroid Build Coastguard Worker             lock(mutex);
158*8975f5c5SAndroid Build Coastguard Worker         }
159*8975f5c5SAndroid Build Coastguard Worker     }
~ScopedContextMutexAddRefLock()160*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ~ScopedContextMutexAddRefLock()
161*8975f5c5SAndroid Build Coastguard Worker     {
162*8975f5c5SAndroid Build Coastguard Worker         if (mMutex != nullptr)
163*8975f5c5SAndroid Build Coastguard Worker         {
164*8975f5c5SAndroid Build Coastguard Worker             mMutex->releaseAndUnlock();
165*8975f5c5SAndroid Build Coastguard Worker         }
166*8975f5c5SAndroid Build Coastguard Worker     }
167*8975f5c5SAndroid Build Coastguard Worker 
168*8975f5c5SAndroid Build Coastguard Worker   private:
169*8975f5c5SAndroid Build Coastguard Worker     void lock(ContextMutex *mutex);
170*8975f5c5SAndroid Build Coastguard Worker 
171*8975f5c5SAndroid Build Coastguard Worker   private:
172*8975f5c5SAndroid Build Coastguard Worker     ContextMutex *mMutex = nullptr;
173*8975f5c5SAndroid Build Coastguard Worker };
174*8975f5c5SAndroid Build Coastguard Worker 
175*8975f5c5SAndroid Build Coastguard Worker class [[nodiscard]] ScopedContextMutexLock final
176*8975f5c5SAndroid Build Coastguard Worker {
177*8975f5c5SAndroid Build Coastguard Worker   public:
178*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexLock() = default;
ScopedContextMutexLock(ContextMutex & mutex)179*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE explicit ScopedContextMutexLock(ContextMutex &mutex) : mMutex(&mutex)
180*8975f5c5SAndroid Build Coastguard Worker     {
181*8975f5c5SAndroid Build Coastguard Worker         mutex.lock();
182*8975f5c5SAndroid Build Coastguard Worker     }
ScopedContextMutexLock(ContextMutex * mutex)183*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexLock(ContextMutex *mutex) : mMutex(mutex)
184*8975f5c5SAndroid Build Coastguard Worker     {
185*8975f5c5SAndroid Build Coastguard Worker         if (ANGLE_LIKELY(mutex != nullptr))
186*8975f5c5SAndroid Build Coastguard Worker         {
187*8975f5c5SAndroid Build Coastguard Worker             mutex->lock();
188*8975f5c5SAndroid Build Coastguard Worker         }
189*8975f5c5SAndroid Build Coastguard Worker     }
~ScopedContextMutexLock()190*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ~ScopedContextMutexLock()
191*8975f5c5SAndroid Build Coastguard Worker     {
192*8975f5c5SAndroid Build Coastguard Worker         if (ANGLE_LIKELY(mMutex != nullptr))
193*8975f5c5SAndroid Build Coastguard Worker         {
194*8975f5c5SAndroid Build Coastguard Worker             mMutex->unlock();
195*8975f5c5SAndroid Build Coastguard Worker         }
196*8975f5c5SAndroid Build Coastguard Worker     }
197*8975f5c5SAndroid Build Coastguard Worker 
ScopedContextMutexLock(ScopedContextMutexLock && other)198*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexLock(ScopedContextMutexLock &&other) : mMutex(other.mMutex)
199*8975f5c5SAndroid Build Coastguard Worker     {
200*8975f5c5SAndroid Build Coastguard Worker         other.mMutex = nullptr;
201*8975f5c5SAndroid Build Coastguard Worker     }
202*8975f5c5SAndroid Build Coastguard Worker     ANGLE_INLINE ScopedContextMutexLock &operator=(ScopedContextMutexLock &&other)
203*8975f5c5SAndroid Build Coastguard Worker     {
204*8975f5c5SAndroid Build Coastguard Worker         std::swap(mMutex, other.mMutex);
205*8975f5c5SAndroid Build Coastguard Worker         return *this;
206*8975f5c5SAndroid Build Coastguard Worker     }
207*8975f5c5SAndroid Build Coastguard Worker 
208*8975f5c5SAndroid Build Coastguard Worker   private:
209*8975f5c5SAndroid Build Coastguard Worker     ContextMutex *mMutex = nullptr;
210*8975f5c5SAndroid Build Coastguard Worker };
211*8975f5c5SAndroid Build Coastguard Worker 
212*8975f5c5SAndroid Build Coastguard Worker }  // namespace egl
213*8975f5c5SAndroid Build Coastguard Worker 
214*8975f5c5SAndroid Build Coastguard Worker #endif  // LIBANGLE_CONTEXT_MUTEX_H_
215