[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