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