1 #include "EXGLContextManager.h" 2 #include <mutex> 3 4 namespace expo { 5 namespace gl_cpp { 6 7 struct ContextState { 8 EXGLContext *ctx; 9 std::shared_mutex mutex; 10 }; 11 12 struct ContextManager { 13 std::unordered_map<EXGLContextId, ContextState> contextMap; 14 std::shared_mutex contextLookupMutex; 15 EXGLContextId nextId = 1; 16 }; 17 18 ContextManager manager; 19 20 // When multiple threads are attempting to establish shared and unique locks on a mutex 21 // we can reach a situation where an unique lock gets priority to run first, but waits 22 // for existing shared locks to be released, while no new shared locks can be acquired. 23 // 24 // When we run ContextPrepare we hold a shared lock, but we also trigger flush on 25 // a different thread which also needs to hold a shared lock. This situation can lead 26 // to deadlock if unique lock have a priority and flush can never start. 27 // 28 // This solution resolves an issue, but introduces a risk that uniqe lock will never 29 // be establish, but given the use-case that should never happen. getUniqueLockSafely(std::shared_mutex & mutex)30std::unique_lock<std::shared_mutex> getUniqueLockSafely(std::shared_mutex &mutex) { 31 std::unique_lock lock(mutex, std::defer_lock); 32 while (!lock.try_lock()) { 33 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 34 } 35 return lock; 36 } 37 ContextGet(EXGLContextId id)38ContextWithLock ContextGet(EXGLContextId id) { 39 std::shared_lock lock(manager.contextLookupMutex); 40 auto iter = manager.contextMap.find(id); 41 // if ctx is null then destroy is in progress 42 if (iter == manager.contextMap.end() || iter->second.ctx == nullptr) { 43 return {nullptr, std::shared_lock<std::shared_mutex>()}; 44 } 45 return {iter->second.ctx, std::shared_lock(iter->second.mutex)}; 46 } 47 ContextCreate()48EXGLContextId ContextCreate() { 49 // Out of ids? 50 if (manager.nextId >= std::numeric_limits<EXGLContextId>::max()) { 51 EXGLSysLog("Ran out of EXGLContext ids!"); 52 return 0; 53 } 54 55 std::unique_lock lock = getUniqueLockSafely(manager.contextLookupMutex); 56 EXGLContextId ctxId = manager.nextId++; 57 if (manager.contextMap.find(ctxId) != manager.contextMap.end()) { 58 EXGLSysLog("Tried to reuse an EXGLContext id. This shouldn't really happen..."); 59 return 0; 60 } 61 manager.contextMap[ctxId].ctx = new EXGLContext(ctxId); 62 return ctxId; 63 } 64 ContextDestroy(EXGLContextId id)65void ContextDestroy(EXGLContextId id) { 66 { 67 std::shared_lock lock(manager.contextLookupMutex); 68 69 auto iter = manager.contextMap.find(id); 70 if (iter != manager.contextMap.end()) { 71 std::unique_lock lock = getUniqueLockSafely(iter->second.mutex); 72 delete iter->second.ctx; 73 iter->second.ctx = nullptr; 74 } 75 } 76 77 std::unique_lock lock = getUniqueLockSafely(manager.contextLookupMutex); 78 auto iter = manager.contextMap.find(id); 79 if (iter != manager.contextMap.end()) { 80 manager.contextMap.erase(iter); 81 } 82 } 83 84 } // namespace gl_cpp 85 } // namespace expo 86