From: Marek Olšák Date: Mon, 20 Feb 2017 14:27:07 +0000 (+0100) Subject: gallium/u_queue: fix random crashes when the app calls exit() X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4aea8fe7e01f2b65ffad3982687f90c8fa941524;p=mesa.git gallium/u_queue: fix random crashes when the app calls exit() This fixes: vdpauinfo: ../lib/CodeGen/TargetPassConfig.cpp:579: virtual void llvm::TargetPassConfig::addMachinePasses(): Assertion `TPI && IPI && "Pass ID not registered!"' failed. v2: use list_head, switch the call order in destroy Cc: 13.0 17.0 Reviewed-by: Nicolai Hähnle --- diff --git a/src/gallium/auxiliary/util/u_queue.c b/src/gallium/auxiliary/util/u_queue.c index 4da5d8e9f8c..52cfc0a4bf2 100644 --- a/src/gallium/auxiliary/util/u_queue.c +++ b/src/gallium/auxiliary/util/u_queue.c @@ -29,6 +29,68 @@ #include "u_string.h" #include "os/os_time.h" +static void util_queue_killall_and_wait(struct util_queue *queue); + +/**************************************************************************** + * Wait for all queues to assert idle when exit() is called. + * + * Otherwise, C++ static variable destructors can be called while threads + * are using the static variables. + */ + +static once_flag atexit_once_flag = ONCE_FLAG_INIT; +static struct list_head queue_list; +pipe_static_mutex(exit_mutex); + +static void +atexit_handler(void) +{ + struct util_queue *iter; + + pipe_mutex_lock(exit_mutex); + /* Wait for all queues to assert idle. */ + LIST_FOR_EACH_ENTRY(iter, &queue_list, head) { + util_queue_killall_and_wait(iter); + } + pipe_mutex_unlock(exit_mutex); +} + +static void +global_init(void) +{ + LIST_INITHEAD(&queue_list); + atexit(atexit_handler); +} + +static void +add_to_atexit_list(struct util_queue *queue) +{ + call_once(&atexit_once_flag, global_init); + + pipe_mutex_lock(exit_mutex); + LIST_ADD(&queue->head, &queue_list); + pipe_mutex_unlock(exit_mutex); +} + +static void +remove_from_atexit_list(struct util_queue *queue) +{ + struct util_queue *iter, *tmp; + + pipe_mutex_lock(exit_mutex); + LIST_FOR_EACH_ENTRY_SAFE(iter, tmp, &queue_list, head) { + if (iter == queue) { + LIST_DEL(&iter->head); + break; + } + } + pipe_mutex_unlock(exit_mutex); +} + +/**************************************************************************** + * util_queue implementation + */ + static void util_queue_fence_signal(struct util_queue_fence *fence) { @@ -104,6 +166,7 @@ static PIPE_THREAD_ROUTINE(util_queue_thread_func, input) queue->jobs[queue->read_idx].job = NULL; queue->read_idx = (queue->read_idx + 1) % queue->max_jobs; } + queue->num_queued = 0; /* reset this when exiting the thread */ pipe_mutex_unlock(queue->lock); return 0; } @@ -157,6 +220,8 @@ util_queue_init(struct util_queue *queue, } } } + + add_to_atexit_list(queue); return true; fail: @@ -173,8 +238,8 @@ fail: return false; } -void -util_queue_destroy(struct util_queue *queue) +static void +util_queue_killall_and_wait(struct util_queue *queue) { unsigned i; @@ -186,6 +251,13 @@ util_queue_destroy(struct util_queue *queue) for (i = 0; i < queue->num_threads; i++) pipe_thread_wait(queue->threads[i]); +} + +void +util_queue_destroy(struct util_queue *queue) +{ + util_queue_killall_and_wait(queue); + remove_from_atexit_list(queue); pipe_condvar_destroy(queue->has_space_cond); pipe_condvar_destroy(queue->has_queued_cond); diff --git a/src/gallium/auxiliary/util/u_queue.h b/src/gallium/auxiliary/util/u_queue.h index 351315c5780..335813f63c1 100644 --- a/src/gallium/auxiliary/util/u_queue.h +++ b/src/gallium/auxiliary/util/u_queue.h @@ -34,6 +34,7 @@ #define U_QUEUE_H #include "os/os_thread.h" +#include "util/list.h" /* Job completion fence. * Put this into your job structure. @@ -66,6 +67,9 @@ struct util_queue { int max_jobs; int write_idx, read_idx; /* ring buffer pointers */ struct util_queue_job *jobs; + + /* for cleanup at exit(), protected by exit_mutex */ + struct list_head head; }; bool util_queue_init(struct util_queue *queue,