util/u_queue: add an option to resize the queue when it's full

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.

Reviewed-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
This commit is contained in:
Marek Olšák 2017-07-10 21:17:04 +02:00
parent 465bb47d6f
commit 59ad769770
2 changed files with 36 additions and 3 deletions

View file

@ -204,6 +204,7 @@ util_queue_init(struct util_queue *queue,
memset(queue, 0, sizeof(*queue));
queue->name = name;
queue->flags = flags;
queue->num_threads = num_threads;
queue->max_jobs = max_jobs;
@ -329,9 +330,39 @@ util_queue_add_job(struct util_queue *queue,
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);

View file

@ -43,6 +43,7 @@ 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.
@ -69,6 +70,7 @@ struct util_queue {
cnd_t has_queued_cond;
cnd_t has_space_cond;
thrd_t *threads;
unsigned flags;
int num_queued;
unsigned num_threads;
int kill_threads;