1/************************************************************************** 2 * 3 * Copyright 2019 Red Hat. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 **************************************************************************/ 25 26/** 27 * compute shader thread pool. 28 * based on threadpool.c but modified heavily to be compute shader tuned. 29 */ 30 31#include "util/u_thread.h" 32#include "util/u_memory.h" 33#include "lp_cs_tpool.h" 34 35static int 36lp_cs_tpool_worker(void *data) 37{ 38 struct lp_cs_tpool *pool = data; 39 struct lp_cs_local_mem lmem; 40 41 memset(&lmem, 0, sizeof(lmem)); 42 mtx_lock(&pool->m); 43 44 while (!pool->shutdown) { 45 struct lp_cs_tpool_task *task; 46 unsigned iter_per_thread; 47 48 while (list_is_empty(&pool->workqueue) && !pool->shutdown) 49 cnd_wait(&pool->new_work, &pool->m); 50 51 if (pool->shutdown) 52 break; 53 54 task = list_first_entry(&pool->workqueue, struct lp_cs_tpool_task, 55 list); 56 57 unsigned this_iter = task->iter_start; 58 59 iter_per_thread = task->iter_per_thread; 60 61 if (task->iter_remainder && 62 task->iter_start + task->iter_remainder == task->iter_total) { 63 task->iter_remainder--; 64 iter_per_thread = 1; 65 } 66 67 task->iter_start += iter_per_thread; 68 69 if (task->iter_start == task->iter_total) 70 list_del(&task->list); 71 72 mtx_unlock(&pool->m); 73 for (unsigned i = 0; i < iter_per_thread; i++) 74 task->work(task->data, this_iter + i, &lmem); 75 76 mtx_lock(&pool->m); 77 task->iter_finished += iter_per_thread; 78 if (task->iter_finished == task->iter_total) 79 cnd_broadcast(&task->finish); 80 } 81 mtx_unlock(&pool->m); 82 FREE(lmem.local_mem_ptr); 83 return 0; 84} 85 86struct lp_cs_tpool * 87lp_cs_tpool_create(unsigned num_threads) 88{ 89 struct lp_cs_tpool *pool = CALLOC_STRUCT(lp_cs_tpool); 90 91 if (!pool) 92 return NULL; 93 94 (void) mtx_init(&pool->m, mtx_plain); 95 cnd_init(&pool->new_work); 96 97 list_inithead(&pool->workqueue); 98 assert (num_threads <= LP_MAX_THREADS); 99 for (unsigned i = 0; i < num_threads; i++) { 100 if (thrd_success != u_thread_create(pool->threads + i, lp_cs_tpool_worker, pool)) { 101 num_threads = i; /* previous thread is max */ 102 break; 103 } 104 } 105 pool->num_threads = num_threads; 106 return pool; 107} 108 109void 110lp_cs_tpool_destroy(struct lp_cs_tpool *pool) 111{ 112 if (!pool) 113 return; 114 115 mtx_lock(&pool->m); 116 pool->shutdown = true; 117 cnd_broadcast(&pool->new_work); 118 mtx_unlock(&pool->m); 119 120 for (unsigned i = 0; i < pool->num_threads; i++) { 121 thrd_join(pool->threads[i], NULL); 122 } 123 124 cnd_destroy(&pool->new_work); 125 mtx_destroy(&pool->m); 126 FREE(pool); 127} 128 129struct lp_cs_tpool_task * 130lp_cs_tpool_queue_task(struct lp_cs_tpool *pool, 131 lp_cs_tpool_task_func work, void *data, int num_iters) 132{ 133 struct lp_cs_tpool_task *task; 134 135 if (pool->num_threads == 0) { 136 struct lp_cs_local_mem lmem; 137 138 memset(&lmem, 0, sizeof(lmem)); 139 for (unsigned t = 0; t < num_iters; t++) { 140 work(data, t, &lmem); 141 } 142 FREE(lmem.local_mem_ptr); 143 return NULL; 144 } 145 task = CALLOC_STRUCT(lp_cs_tpool_task); 146 if (!task) { 147 return NULL; 148 } 149 150 task->work = work; 151 task->data = data; 152 task->iter_total = num_iters; 153 154 task->iter_per_thread = num_iters / pool->num_threads; 155 task->iter_remainder = num_iters % pool->num_threads; 156 157 cnd_init(&task->finish); 158 159 mtx_lock(&pool->m); 160 161 list_addtail(&task->list, &pool->workqueue); 162 163 cnd_broadcast(&pool->new_work); 164 mtx_unlock(&pool->m); 165 return task; 166} 167 168void 169lp_cs_tpool_wait_for_task(struct lp_cs_tpool *pool, 170 struct lp_cs_tpool_task **task_handle) 171{ 172 struct lp_cs_tpool_task *task = *task_handle; 173 174 if (!pool || !task) 175 return; 176 177 mtx_lock(&pool->m); 178 while (task->iter_finished < task->iter_total) 179 cnd_wait(&task->finish, &pool->m); 180 mtx_unlock(&pool->m); 181 182 cnd_destroy(&task->finish); 183 FREE(task); 184 *task_handle = NULL; 185} 186