• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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