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)30 std::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)38 ContextWithLock 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()48 EXGLContextId 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)65 void 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