[Mesa-dev] [PATCH 1/2] llvmpipe: Pool the LLVMContexts in use.

Mathias Fröhlich Mathias.Froehlich at gmx.net
Sun Jul 13 09:13:52 PDT 2014


This is one step to make llvmpipe thread safe as mandated by the OpenGL
standard. Using the global LLVMContext is obviously a problem for
that kind of use pattern. Allocating and disposing a gallivm_state
private LLVMContext leads to problems with accumulating LLVMContext
dependent cached instances somewhere in llvms internals.
For more details about that see http://llvm.org/bugs/show_bug.cgi?id=20124.
So, given that, having a gallivm_state private LLVMContext that is
returned into a pool of LLVMContext instances for reuse seems to avoid
this accumulation and still allows concurrent access.

Signed-off-by: Mathias Froehlich <Mathias.Froehlich at web.de>
---
 src/gallium/auxiliary/gallivm/lp_bld_init.c | 80 +++++++++++++++++++++++++----
 1 file changed, 70 insertions(+), 10 deletions(-)

diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.c b/src/gallium/auxiliary/gallivm/lp_bld_init.c
index 8b8686d..3a9d10f 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_init.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_init.c
@@ -32,6 +32,7 @@
 #include "util/u_debug.h"
 #include "util/u_memory.h"
 #include "util/u_simple_list.h"
+#include "os/os_thread.h"
 #include "os/os_time.h"
 #include "lp_bld.h"
 #include "lp_bld_debug.h"
@@ -74,11 +75,73 @@ void LLVMLinkInMCJIT();
  * owned by the context, so if we freeing contexts causes
  * memory leaks and false cache hits when these objects are destroyed.
  *
- * TODO: For thread safety on multi-threaded OpenGL we should use one LLVM
- * context per thread, and put them in a pool when threads are destroyed.
+ * Namely, lib/CodeGen/SelectionDAG/SelectionDAG.cpp holds a static
+ * cache of llvm::EVT instances that belong to a LLVMContext. The cached
+ * llvm::EVT instances are keyed there by their LLVMContext pointer address
+ * as well as by their internal type enumeration value.
+ * By that we introduce massive leaks filling up this static cache
+ * if we use newly created LLVMContext instances that are destroyed
+ * after use.
  */
-#define USE_GLOBAL_CONTEXT 1
 
+struct llvm_context_pool_node {
+   struct llvm_context_pool_node *prev, *next;
+   LLVMContextRef context;
+};
+
+/* Protect the two lists below */
+pipe_static_mutex(llvm_pool_mutex);
+/* Contains all the LLVMContexts currently not in use. */
+static struct llvm_context_pool_node llvm_context_pool = {
+   &llvm_context_pool, &llvm_context_pool, NULL
+};
+/*
+ * Contains all the llvm_context_pool_node's currently not in use
+ * in llvm_context_pool pool.
+ */
+static struct llvm_context_pool_node llvm_empty_node_pool = {
+   &llvm_empty_node_pool, &llvm_empty_node_pool, NULL
+};
+
+static LLVMContextRef get_llvm_context()
+{
+   LLVMContextRef context = NULL;
+
+   pipe_mutex_lock(llvm_pool_mutex);
+
+   if (!is_empty_list(&llvm_context_pool)) {
+      struct llvm_context_pool_node *element;
+      element = first_elem(&llvm_context_pool);
+      remove_from_list(element);
+      context = element->context;
+      element->context = NULL;
+      insert_at_head(&llvm_empty_node_pool, element);
+   }
+
+   pipe_mutex_unlock(llvm_pool_mutex);
+
+   if (!context)
+      context = LLVMContextCreate();
+
+   return context;
+}
+
+static void put_llvm_context(LLVMContextRef context)
+{
+   pipe_mutex_lock(llvm_pool_mutex);
+
+   struct llvm_context_pool_node *element;
+   if (is_empty_list(&llvm_empty_node_pool)) {
+      element = CALLOC_STRUCT(llvm_context_pool_node);
+   } else {
+      element = first_elem(&llvm_empty_node_pool);
+      remove_from_list(element);
+   }
+   element->context = context;
+   insert_at_head(&llvm_context_pool, element);
+
+   pipe_mutex_unlock(llvm_pool_mutex);
+}
 
 #ifdef DEBUG
 unsigned gallivm_debug = 0;
@@ -193,8 +256,8 @@ gallivm_free_ir(struct gallivm_state *gallivm)
    if (gallivm->builder)
       LLVMDisposeBuilder(gallivm->builder);
 
-   if (!USE_GLOBAL_CONTEXT && gallivm->context)
-      LLVMContextDispose(gallivm->context);
+   if (gallivm->context)
+      put_llvm_context(gallivm->context);
 
    gallivm->engine = NULL;
    gallivm->target = NULL;
@@ -202,6 +265,7 @@ gallivm_free_ir(struct gallivm_state *gallivm)
    gallivm->passmgr = NULL;
    gallivm->context = NULL;
    gallivm->builder = NULL;
+   gallivm->context = NULL;
 }
 
 
@@ -292,11 +356,7 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name)
 
    lp_build_init();
 
-   if (USE_GLOBAL_CONTEXT) {
-      gallivm->context = LLVMGetGlobalContext();
-   } else {
-      gallivm->context = LLVMContextCreate();
-   }
+   gallivm->context = get_llvm_context();
    if (!gallivm->context)
       goto fail;
 
-- 
1.9.3



More information about the mesa-dev mailing list