gallium/u_queue: rewrite util_queue_fence to allow multiple waiters
authorMarek Olšák <marek.olsak@amd.com>
Sat, 11 Jun 2016 15:51:22 +0000 (17:51 +0200)
committerMarek Olšák <marek.olsak@amd.com>
Fri, 24 Jun 2016 10:24:40 +0000 (12:24 +0200)
Checking "signalled" is first done without a mutex, then with a mutex.
Also, checking without waiting doesn't lock the mutex. This is racy, but
should be safe.

Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
src/gallium/auxiliary/util/u_queue.c
src/gallium/auxiliary/util/u_queue.h

index 2b6850d02e4bd80f9b0408f9553d002a3dafd03c..7a67c1169d9b3c5349dd1b50e3d77639293e59f5 100644 (file)
  */
 
 #include "u_queue.h"
+#include "os/os_time.h"
+
+static void
+util_queue_fence_signal(struct util_queue_fence *fence)
+{
+   pipe_mutex_lock(fence->mutex);
+   fence->signalled = true;
+   pipe_condvar_broadcast(fence->cond);
+   pipe_mutex_unlock(fence->mutex);
+}
+
+void
+util_queue_job_wait(struct util_queue_fence *fence)
+{
+   if (fence->signalled)
+      return;
+
+   pipe_mutex_lock(fence->mutex);
+   while (!fence->signalled)
+      pipe_condvar_wait(fence->cond, fence->mutex);
+   pipe_mutex_unlock(fence->mutex);
+}
 
 static PIPE_THREAD_ROUTINE(util_queue_thread_func, param)
 {
@@ -47,14 +69,15 @@ static PIPE_THREAD_ROUTINE(util_queue_thread_func, param)
 
       if (job.job) {
          queue->execute_job(job.job);
-         pipe_semaphore_signal(&job.fence->done);
+         util_queue_fence_signal(job.fence);
       }
    }
 
    /* signal remaining jobs before terminating */
    pipe_mutex_lock(queue->lock);
    while (queue->jobs[queue->read_idx].job) {
-      pipe_semaphore_signal(&queue->jobs[queue->read_idx].fence->done);
+      util_queue_fence_signal(queue->jobs[queue->read_idx].fence);
+
       queue->jobs[queue->read_idx].job = NULL;
       queue->read_idx = (queue->read_idx + 1) % queue->max_jobs;
    }
@@ -113,13 +136,17 @@ util_queue_destroy(struct util_queue *queue)
 void
 util_queue_fence_init(struct util_queue_fence *fence)
 {
-   pipe_semaphore_init(&fence->done, 1);
+   memset(fence, 0, sizeof(*fence));
+   pipe_mutex_init(fence->mutex);
+   pipe_condvar_init(fence->cond);
+   fence->signalled = true;
 }
 
 void
 util_queue_fence_destroy(struct util_queue_fence *fence)
 {
-   pipe_semaphore_destroy(&fence->done);
+   pipe_condvar_destroy(fence->cond);
+   pipe_mutex_destroy(fence->mutex);
 }
 
 void
@@ -128,8 +155,9 @@ util_queue_add_job(struct util_queue *queue,
                    struct util_queue_fence *fence)
 {
    struct util_queue_job *ptr;
-   /* Set the semaphore to "busy". */
-   pipe_semaphore_wait(&fence->done);
+
+   assert(fence->signalled);
+   fence->signalled = false;
 
    /* if the queue is full, wait until there is space */
    pipe_semaphore_wait(&queue->has_space);
@@ -143,12 +171,3 @@ util_queue_add_job(struct util_queue *queue,
    pipe_mutex_unlock(queue->lock);
    pipe_semaphore_signal(&queue->queued);
 }
-
-void
-util_queue_job_wait(struct util_queue_fence *fence)
-{
-   /* wait and set the semaphore to "busy" */
-   pipe_semaphore_wait(&fence->done);
-   /* set the semaphore to "idle" */
-   pipe_semaphore_signal(&fence->done);
-}
index 48cd9f4c7070f791cabe79713d382192faa37b5e..acebb51382f9b09e27fa0e8ad5a01e3780fe14a0 100644 (file)
@@ -39,7 +39,9 @@
  * Put this into your job structure.
  */
 struct util_queue_fence {
-   pipe_semaphore done;
+   pipe_mutex mutex;
+   pipe_condvar cond;
+   int signalled;
 };
 
 struct util_queue_job {
@@ -79,4 +81,10 @@ util_queue_is_initialized(struct util_queue *queue)
    return queue->thread != 0;
 }
 
+static inline bool
+util_queue_fence_is_signalled(struct util_queue_fence *fence)
+{
+   return fence->signalled != 0;
+}
+
 #endif