gallium/u_queue: fix random crashes when the app calls exit()
authorMarek Olšák <marek.olsak@amd.com>
Mon, 20 Feb 2017 14:27:07 +0000 (15:27 +0100)
committerMarek Olšák <marek.olsak@amd.com>
Wed, 22 Feb 2017 19:26:39 +0000 (20:26 +0100)
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 <mesa-stable@lists.freedesktop.org>
Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
src/gallium/auxiliary/util/u_queue.c
src/gallium/auxiliary/util/u_queue.h

index 4da5d8e9f8c3fefaf2e6c406aea4a15f0ce70c61..52cfc0a4bf2820755861fffbaf92b9d6c82af097 100644 (file)
 #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);
index 351315c5780f2c0662b1e173bbe5a967f02b273f..335813f63c1c23be824659e6ba13f2dbe16afd4c 100644 (file)
@@ -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,