[magma] Add locking to support multiple client threads.

Change-Id: I0cb15d9b5092d12bda3b145fa8fc76bf387eb06a
diff --git a/src/graphics/lib/magma/src/libmagma_gfxstream/magma.cc b/src/graphics/lib/magma/src/libmagma_gfxstream/magma.cc
index 96cde2c..a064a0c 100644
--- a/src/graphics/lib/magma/src/libmagma_gfxstream/magma.cc
+++ b/src/graphics/lib/magma/src/libmagma_gfxstream/magma.cc
@@ -45,6 +45,8 @@
 
   magma_status_t get_fd_for_buffer(magma_buffer_t buffer, int* fd_out);
 
+  std::mutex& mutex() { return m_mutex_; }
+
   static magma_status_t magma_device_import(void* self, magma_handle_t device_channel,
                                             magma_device_t* device_out);
   static magma_status_t magma_query(void* self, magma_device_t device, uint64_t id,
@@ -53,11 +55,42 @@
                                                  magma_handle_t* handle_out);
   static magma_status_t magma_poll(void* self, magma_poll_item_t* items, uint32_t count, uint64_t timeout_ns);
 
+  static void set_thread_local_context_lock(std::unique_lock<std::mutex>* lock) {
+    t_lock = lock;
+  }
+
+  static std::unique_lock<std::mutex>* get_thread_local_context_lock() {
+    return t_lock;
+  }
+
   magma_device_import_client_proc_t magma_device_import_enc_;
   magma_query_client_proc_t magma_query_enc_;
   magma_poll_client_proc_t magma_poll_enc_;
+
+  std::mutex m_mutex_;
+  static thread_local std::unique_lock<std::mutex>* t_lock;
 };
 
+// This makes the mutex lock available to decoding methods that can take time
+// (eg magma_poll), to prevent one thread from locking out others.
+class ContextLock {
+public:
+  ContextLock(MagmaClientContext* context) : m_context_(context), m_lock_(context->mutex()) {
+    m_context_->set_thread_local_context_lock(&m_lock_);
+  }
+
+  ~ContextLock() {
+    m_context_->set_thread_local_context_lock(nullptr);
+  }
+
+private:
+  MagmaClientContext* m_context_;
+  std::unique_lock<std::mutex> m_lock_;
+};
+
+// static
+thread_local std::unique_lock<std::mutex>* MagmaClientContext::t_lock;
+
 MagmaClientContext::MagmaClientContext(AddressSpaceStream* stream)
     : magma_encoder_context_t(stream, new ChecksumCalculator) {
   magma_device_import_enc_ = magma_client_context_t::magma_device_import;
@@ -267,6 +300,9 @@
        if (status != MAGMA_STATUS_TIMED_OUT)
           return status;
 
+       // Not ready, allow other threads to work in with us
+       get_thread_local_context_lock()->unlock();
+
        std::this_thread::yield();
 
        int64_t time_now = static_cast<int64_t>(get_ns_monotonic(false));
@@ -279,12 +315,16 @@
 
        if (time_now >= abs_timeout_ns)
           break;
+
+       get_thread_local_context_lock()->lock();
     }
 
     return MAGMA_STATUS_TIMED_OUT;
 }
 
-magma_client_context_t* GetMagmaContext() {
+// We have a singleton client context for all threads.  We want all client
+// threads served by a single server RenderThread.
+MagmaClientContext* GetMagmaContext() {
   static MagmaClientContext* s_context;
   static std::once_flag once_flag;
 
@@ -317,7 +357,10 @@
 }
 
 // Used in magma_entry.cpp
-#define GET_CONTEXT magma_client_context_t* ctx = GetMagmaContext()
+// Always lock around the encoding methods because we have a singleton context.
+#define GET_CONTEXT \
+  MagmaClientContext* ctx = GetMagmaContext(); \
+  ContextLock lock(ctx)
 
 #include "gen/magma_entry.cpp"