[Mesa-dev] [PATCH 07/10] util/u_queue: add an option to resize the queue when it's full
Marek Olšák
maraeo at gmail.com
Mon Jul 10 21:21:42 UTC 2017
From: Marek Olšák <marek.olsak at amd.com>
Consider the following situation:
mtx_lock(mutex);
do_something();
util_queue_add_job(...);
mtx_unlock(mutex);
If the queue is full, util_queue_add_job will wait for a free slot.
If the job which is currently being executed tries to lock the mutex,
it will be stuck forever, because util_queue_add_job is stuck.
The deadlock can be trivially resolved by increasing the queue size
(reallocating the queue) in util_queue_add_job if the queue is full.
Then util_queue_add_job becomes wait-free.
radeonsi will use it.
---
src/util/u_queue.c | 37 ++++++++++++++++++++++++++++++++++---
src/util/u_queue.h | 2 ++
2 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/src/util/u_queue.c b/src/util/u_queue.c
index cb59030..49361c3 100644
--- a/src/util/u_queue.c
+++ b/src/util/u_queue.c
@@ -197,20 +197,21 @@ bool
util_queue_init(struct util_queue *queue,
const char *name,
unsigned max_jobs,
unsigned num_threads,
unsigned flags)
{
unsigned i;
memset(queue, 0, sizeof(*queue));
queue->name = name;
+ queue->flags = flags;
queue->num_threads = num_threads;
queue->max_jobs = max_jobs;
queue->jobs = (struct util_queue_job*)
calloc(max_jobs, sizeof(struct util_queue_job));
if (!queue->jobs)
goto fail;
(void) mtx_init(&queue->lock, mtx_plain);
@@ -322,23 +323,53 @@ util_queue_add_job(struct util_queue *queue,
/* well no good option here, but any leaks will be
* short-lived as things are shutting down..
*/
return;
}
fence->signalled = false;
assert(queue->num_queued >= 0 && queue->num_queued <= queue->max_jobs);
- /* if the queue is full, wait until there is space */
- while (queue->num_queued == queue->max_jobs)
- cnd_wait(&queue->has_space_cond, &queue->lock);
+ if (queue->num_queued == queue->max_jobs) {
+ if (queue->flags & UTIL_QUEUE_INIT_RESIZE_IF_FULL) {
+ /* If the queue is full, make it larger to avoid waiting for a free
+ * slot.
+ */
+ unsigned new_max_jobs = queue->max_jobs + 8;
+ struct util_queue_job *jobs =
+ (struct util_queue_job*)calloc(new_max_jobs,
+ sizeof(struct util_queue_job));
+ assert(jobs);
+
+ /* Copy all queued jobs into the new list. */
+ unsigned num_jobs = 0;
+ unsigned i = queue->read_idx;
+
+ do {
+ jobs[num_jobs++] = queue->jobs[i];
+ i = (i + 1) % queue->max_jobs;
+ } while (i != queue->write_idx);
+
+ assert(num_jobs == queue->num_queued);
+
+ free(queue->jobs);
+ queue->jobs = jobs;
+ queue->read_idx = 0;
+ queue->write_idx = num_jobs;
+ queue->max_jobs = new_max_jobs;
+ } else {
+ /* Wait until there is a free slot. */
+ while (queue->num_queued == queue->max_jobs)
+ cnd_wait(&queue->has_space_cond, &queue->lock);
+ }
+ }
ptr = &queue->jobs[queue->write_idx];
assert(ptr->job == NULL);
ptr->job = job;
ptr->fence = fence;
ptr->execute = execute;
ptr->cleanup = cleanup;
queue->write_idx = (queue->write_idx + 1) % queue->max_jobs;
queue->num_queued++;
diff --git a/src/util/u_queue.h b/src/util/u_queue.h
index edd6bab..ff713ae 100644
--- a/src/util/u_queue.h
+++ b/src/util/u_queue.h
@@ -36,20 +36,21 @@
#include <string.h>
#include "util/list.h"
#include "util/u_thread.h"
#ifdef __cplusplus
extern "C" {
#endif
#define UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY (1 << 0)
+#define UTIL_QUEUE_INIT_RESIZE_IF_FULL (1 << 1)
/* Job completion fence.
* Put this into your job structure.
*/
struct util_queue_fence {
mtx_t mutex;
cnd_t cond;
int signalled;
};
@@ -62,20 +63,21 @@ struct util_queue_job {
util_queue_execute_func cleanup;
};
/* Put this into your context. */
struct util_queue {
const char *name;
mtx_t lock;
cnd_t has_queued_cond;
cnd_t has_space_cond;
thrd_t *threads;
+ unsigned flags;
int num_queued;
unsigned num_threads;
int kill_threads;
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;
};
--
2.7.4
More information about the mesa-dev
mailing list