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
35 static int
lp_cs_tpool_worker(void * data)36 lp_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
86 struct lp_cs_tpool *
lp_cs_tpool_create(unsigned num_threads)87 lp_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
109 void
lp_cs_tpool_destroy(struct lp_cs_tpool * pool)110 lp_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
129 struct lp_cs_tpool_task *
lp_cs_tpool_queue_task(struct lp_cs_tpool * pool,lp_cs_tpool_task_func work,void * data,int num_iters)130 lp_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
168 void
lp_cs_tpool_wait_for_task(struct lp_cs_tpool * pool,struct lp_cs_tpool_task ** task_handle)169 lp_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