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