blob: d6ed8c16606244180d05e85e311b0b7ba548b5a7 [file] [log] [blame]
Cody Northrop0d5881e2014-09-17 14:06:55 -06001/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2014 LunarG, Inc. 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
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <stdio.h>
26#include <stdbool.h>
27#include "c11/threads.h"
28#include "main/compiler.h"
29#include "main/simple_list.h"
30#include "threadpool.h"
31
32enum _mesa_threadpool_control {
33 MESA_THREADPOOL_NORMAL, /* threads wait when there is no task */
34 MESA_THREADPOOL_QUIT, /* threads quit when there is no task */
35 MESA_THREADPOOL_QUIT_NOW /* threads quit as soon as possible */
36};
37
38enum _mesa_threadpool_task_state {
39 MESA_THREADPOOL_TASK_PENDING, /* task is on the pending list */
40 MESA_THREADPOOL_TASK_ACTIVE, /* task is being worked on */
41 MESA_THREADPOOL_TASK_COMPLETED, /* task has been completed */
42 MESA_THREADPOOL_TASK_CANCELLED /* task is cancelled */
43};
44
45struct _mesa_threadpool_task {
46 /* these are protected by the pool's mutex */
47 struct simple_node link; /* must be the first */
48 enum _mesa_threadpool_task_state state;
49 cnd_t completed;
50
51 void (*func)(void *);
52 void *data;
53};
54
55struct _mesa_threadpool {
56 mtx_t mutex;
57 int refcnt;
58 bool shutdown;
59
60 enum _mesa_threadpool_control thread_control;
61 thrd_t *threads;
62 int num_threads, max_threads;
63 int idle_threads; /* number of threads that are idle */
64 cnd_t thread_wakeup;
65 cnd_t thread_joined;
66
67 struct simple_node pending_tasks;
68 int num_pending_tasks;
69 int num_tasks;
70};
71
72static struct _mesa_threadpool_task *
73task_create(void)
74{
75 struct _mesa_threadpool_task *task;
76
77 task = malloc(sizeof(*task));
78 if (!task)
79 return NULL;
80
81 if (cnd_init(&task->completed)) {
82 free(task);
83 return NULL;
84 }
85
86 task->state = MESA_THREADPOOL_TASK_PENDING;
87
88 return task;
89}
90
91static void
92task_destroy(struct _mesa_threadpool_task *task)
93{
94 cnd_destroy(&task->completed);
95 free(task);
96}
97
98static void
99pool_exec_task(struct _mesa_threadpool *pool,
100 struct _mesa_threadpool_task *task)
101{
102 assert(task->state == MESA_THREADPOOL_TASK_PENDING);
103
104 remove_from_list(&task->link);
105 pool->num_pending_tasks--;
106
107 task->state = MESA_THREADPOOL_TASK_ACTIVE;
108
109 /* do the work! */
110 mtx_unlock(&pool->mutex);
111 task->func(task->data);
112 mtx_lock(&pool->mutex);
113
114 task->state = MESA_THREADPOOL_TASK_COMPLETED;
115}
116
117static int
118_mesa_threadpool_worker(void *arg)
119{
120 struct _mesa_threadpool *pool = (struct _mesa_threadpool *) arg;
121
122 mtx_lock(&pool->mutex);
123
124 while (true) {
125 struct _mesa_threadpool_task *task;
126
127 /* wait until there are tasks */
128 while (is_empty_list(&pool->pending_tasks) &&
129 pool->thread_control == MESA_THREADPOOL_NORMAL) {
130 pool->idle_threads++;
131 cnd_wait(&pool->thread_wakeup, &pool->mutex);
132 pool->idle_threads--;
133 }
134
135 if (pool->thread_control != MESA_THREADPOOL_NORMAL) {
136 if (pool->thread_control == MESA_THREADPOOL_QUIT_NOW ||
137 is_empty_list(&pool->pending_tasks))
138 break;
139 }
140
141 assert(!is_empty_list(&pool->pending_tasks));
142 task = (struct _mesa_threadpool_task *)
143 first_elem(&pool->pending_tasks);
144
145 pool_exec_task(pool, task);
146 cnd_signal(&task->completed);
147 }
148
149 mtx_unlock(&pool->mutex);
150
151 return 0;
152}
153
154/**
155 * Queue a new task.
156 */
157struct _mesa_threadpool_task *
158_mesa_threadpool_queue_task(struct _mesa_threadpool *pool,
159 void (*func)(void *), void *data)
160{
161 struct _mesa_threadpool_task *task;
162
163 task = task_create();
164 if (!task)
165 return NULL;
166
167 task->func = func;
168 task->data = data;
169
170 mtx_lock(&pool->mutex);
171
172 if (unlikely(pool->shutdown)) {
173 mtx_unlock(&pool->mutex);
174 free(task);
175 return NULL;
176 }
177
178 /* someone is joining with the threads */
179 while (unlikely(pool->thread_control != MESA_THREADPOOL_NORMAL))
180 cnd_wait(&pool->thread_joined, &pool->mutex);
181
182 /* spawn threads as needed */
183 if (pool->idle_threads <= pool->num_pending_tasks &&
184 pool->num_threads < pool->max_threads) {
185 int err;
186
187 err = thrd_create(&pool->threads[pool->num_threads],
188 _mesa_threadpool_worker, (void *) pool);
189 if (!err)
190 pool->num_threads++;
191
192 if (!pool->num_threads) {
193 mtx_unlock(&pool->mutex);
194 task_destroy(task);
195 return NULL;
196 }
197 }
198
199 insert_at_tail(&pool->pending_tasks, &task->link);
200 pool->num_tasks++;
201 pool->num_pending_tasks++;
202 cnd_signal(&pool->thread_wakeup);
203
204 mtx_unlock(&pool->mutex);
205
206 return task;
207}
208
209/**
210 * Wait and destroy the tasks.
211 */
212static bool
213pool_wait_tasks(struct _mesa_threadpool *pool,
214 struct _mesa_threadpool_task **tasks,
215 int num_tasks)
216{
217 bool all_completed = true;
218 int i;
219
220 for (i = 0; i < num_tasks; i++) {
221 struct _mesa_threadpool_task *task = tasks[i];
222
223 while (task->state != MESA_THREADPOOL_TASK_COMPLETED &&
224 task->state != MESA_THREADPOOL_TASK_CANCELLED)
225 cnd_wait(&task->completed, &pool->mutex);
226
227 if (task->state != MESA_THREADPOOL_TASK_COMPLETED)
228 all_completed = false;
229
230 task_destroy(task);
231 }
232
233 pool->num_tasks -= num_tasks;
234
235 return all_completed;
236}
237
238/**
239 * Wait for \p tasks to complete, and destroy them. If some of \p tasks
240 * cannot not be completed, return false.
241 *
242 * This function can be called from within the worker threads.
243 */
244bool
245_mesa_threadpool_complete_tasks(struct _mesa_threadpool *pool,
246 struct _mesa_threadpool_task **tasks,
247 int num_tasks)
248{
249 bool prioritized = false, completed;
250 int i;
251
252 mtx_lock(&pool->mutex);
253
254 /* we need to do something about tasks that are pending */
255 for (i = 0; i < num_tasks; i++) {
256 struct _mesa_threadpool_task *task = tasks[i];
257
258 if (task->state != MESA_THREADPOOL_TASK_PENDING)
259 continue;
260
261 /* move them to the head so that they are executed next */
262 if (!prioritized) {
263 int j;
264
265 for (j = i + 1; j < num_tasks; j++) {
266 if (task->state == MESA_THREADPOOL_TASK_PENDING)
267 move_to_head(&pool->pending_tasks, &task->link);
268 }
269 prioritized = true;
270 }
271
272 /*
273 * Execute right away for we would have to wait for the worker threads
274 * otherwise, which is no faster. More importantly, when this is called
275 * from within worker threads, there may be no idle thread available to
276 * execute them.
277 */
278 pool_exec_task(pool, task);
279 }
280
281 completed = pool_wait_tasks(pool, tasks, num_tasks);
282
283 mtx_unlock(&pool->mutex);
284
285 return completed;
286}
287
288/**
289 * This is equivalent to calling \p _mesa_threadpool_complete_tasks with one
290 * task.
291 */
292bool
293_mesa_threadpool_complete_task(struct _mesa_threadpool *pool,
294 struct _mesa_threadpool_task *task)
295{
296 bool completed;
297
298 mtx_lock(&pool->mutex);
299
300 if (task->state == MESA_THREADPOOL_TASK_PENDING)
301 pool_exec_task(pool, task);
302
303 completed = pool_wait_tasks(pool, &task, 1);
304
305 mtx_unlock(&pool->mutex);
306
307 return completed;
308}
309
310static void
311pool_cancel_pending_tasks(struct _mesa_threadpool *pool)
312{
313 struct simple_node *node, *temp;
314
315 if (is_empty_list(&pool->pending_tasks))
316 return;
317
318 foreach_s(node, temp, &pool->pending_tasks) {
319 struct _mesa_threadpool_task *task =
320 (struct _mesa_threadpool_task *) node;
321
322 remove_from_list(&task->link);
323 task->state = MESA_THREADPOOL_TASK_CANCELLED;
324
325 /* in case some thread is already waiting */
326 cnd_signal(&task->completed);
327 }
328
329 pool->num_pending_tasks = 0;
330}
331
332static void
333pool_join_threads(struct _mesa_threadpool *pool, bool graceful)
334{
335 int joined_threads = 0;
336
337 if (!pool->num_threads)
338 return;
339
340 pool->thread_control = (graceful) ?
341 MESA_THREADPOOL_QUIT : MESA_THREADPOOL_QUIT_NOW;
342
343 while (joined_threads < pool->num_threads) {
344 int i = joined_threads, num_threads = pool->num_threads;
345
346 cnd_broadcast(&pool->thread_wakeup);
347 mtx_unlock(&pool->mutex);
348 while (i < num_threads)
349 thrd_join(pool->threads[i++], NULL);
350 mtx_lock(&pool->mutex);
351
352 joined_threads = num_threads;
353 }
354
355 pool->thread_control = MESA_THREADPOOL_NORMAL;
356 pool->num_threads = 0;
357 assert(!pool->idle_threads);
358}
359
360/**
361 * Join with all pool threads. When \p graceful is true, wait for the pending
362 * tasks to be completed.
363 */
364void
365_mesa_threadpool_join(struct _mesa_threadpool *pool, bool graceful)
366{
367 mtx_lock(&pool->mutex);
368
369 /* someone is already joining with the threads */
370 while (unlikely(pool->thread_control != MESA_THREADPOOL_NORMAL))
371 cnd_wait(&pool->thread_joined, &pool->mutex);
372
373 if (pool->num_threads) {
374 pool_join_threads(pool, graceful);
375 /* wake up whoever is waiting */
376 cnd_broadcast(&pool->thread_joined);
377 }
378
379 if (!graceful)
380 pool_cancel_pending_tasks(pool);
381
382 assert(pool->num_threads == 0);
383 assert(is_empty_list(&pool->pending_tasks) && !pool->num_pending_tasks);
384
385 mtx_unlock(&pool->mutex);
386}
387
388/**
389 * After this call, no task can be queued.
390 */
391static void
392_mesa_threadpool_set_shutdown(struct _mesa_threadpool *pool)
393{
394 mtx_lock(&pool->mutex);
395 pool->shutdown = true;
396 mtx_unlock(&pool->mutex);
397}
398
399/**
400 * Decrease the reference count. Destroy \p pool when the reference count
401 * reaches zero.
402 */
403void
404_mesa_threadpool_unref(struct _mesa_threadpool *pool)
405{
406 bool destroy = false;
407
408 mtx_lock(&pool->mutex);
409 pool->refcnt--;
410 destroy = (pool->refcnt == 0);
411 mtx_unlock(&pool->mutex);
412
413 if (destroy) {
414 _mesa_threadpool_join(pool, false);
415
416 if (pool->num_tasks) {
417 fprintf(stderr, "thread pool destroyed with %d tasks\n",
418 pool->num_tasks);
419 }
420
421 free(pool->threads);
422 cnd_destroy(&pool->thread_joined);
423 cnd_destroy(&pool->thread_wakeup);
424 mtx_destroy(&pool->mutex);
425 free(pool);
426 }
427}
428
429/**
430 * Increase the reference count.
431 */
432struct _mesa_threadpool *
433_mesa_threadpool_ref(struct _mesa_threadpool *pool)
434{
435 mtx_lock(&pool->mutex);
436 pool->refcnt++;
437 mtx_unlock(&pool->mutex);
438
439 return pool;
440}
441
442/**
443 * Create a thread pool. As threads are spawned as needed, this is
444 * inexpensive.
445 */
446struct _mesa_threadpool *
447_mesa_threadpool_create(int max_threads)
448{
449 struct _mesa_threadpool *pool;
450
451 if (max_threads < 1)
452 return NULL;
453
454 pool = calloc(1, sizeof(*pool));
455 if (!pool)
456 return NULL;
457
458 if (mtx_init(&pool->mutex, mtx_plain)) {
459 free(pool);
460 return NULL;
461 }
462
463 pool->refcnt = 1;
464
465 if (cnd_init(&pool->thread_wakeup)) {
466 mtx_destroy(&pool->mutex);
467 free(pool);
468 return NULL;
469 }
470
471 if (cnd_init(&pool->thread_joined)) {
472 cnd_destroy(&pool->thread_wakeup);
473 mtx_destroy(&pool->mutex);
474 free(pool);
475 return NULL;
476 }
477
478 pool->thread_control = MESA_THREADPOOL_NORMAL;
479
480 pool->threads = malloc(sizeof(pool->threads[0]) * max_threads);
481 if (!pool->threads) {
482 cnd_destroy(&pool->thread_joined);
483 cnd_destroy(&pool->thread_wakeup);
484 mtx_destroy(&pool->mutex);
485 free(pool);
486 return NULL;
487 }
488
489 pool->max_threads = max_threads;
490
491 make_empty_list(&pool->pending_tasks);
492
493 return pool;
494}
495
496static mtx_t threadpool_lock = _MTX_INITIALIZER_NP;
497static struct _mesa_threadpool *threadpool;
498
499/**
500 * Get the singleton GLSL thread pool. \p max_threads is honored only by the
501 * first call to this function.
502 */
503struct _mesa_threadpool *
504_mesa_glsl_get_threadpool(int max_threads)
505{
506 mtx_lock(&threadpool_lock);
507 if (!threadpool)
508 threadpool = _mesa_threadpool_create(max_threads);
509 if (threadpool)
510 _mesa_threadpool_ref(threadpool);
511 mtx_unlock(&threadpool_lock);
512
513 return threadpool;
514}
515
516/**
517 * Wait until all tasks are completed and threads are joined.
518 */
519void
520_mesa_glsl_wait_threadpool(void)
521{
522 mtx_lock(&threadpool_lock);
523 if (threadpool)
524 _mesa_threadpool_join(threadpool, true);
525 mtx_unlock(&threadpool_lock);
526}
527
528/**
529 * Destroy the GLSL thread pool.
530 */
531void
532_mesa_glsl_destroy_threadpool(void)
533{
534 mtx_lock(&threadpool_lock);
535 if (threadpool) {
536 /*
537 * This is called from _mesa_destroy_shader_compiler(). No new task is
538 * allowed since this point. But contexts, who also own references to
539 * the pool, can still complete tasks that have been queued.
540 */
541 _mesa_threadpool_set_shutdown(threadpool);
542
543 _mesa_threadpool_join(threadpool, false);
544 _mesa_threadpool_unref(threadpool);
545 threadpool = NULL;
546 }
547 mtx_unlock(&threadpool_lock);
548}