1 /*
2 * Copyright © 2012 Intel Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "swapchain9.h"
7 #include "surface9.h"
8 #include "device9.h"
9
10 #include "nine_helpers.h"
11 #include "nine_pipe.h"
12 #include "nine_dump.h"
13
14 #include "util/u_inlines.h"
15 #include "util/u_surface.h"
16 #include "hud/hud_context.h"
17 #include "frontend/drm_driver.h"
18
19 #include "util/u_thread.h"
20 #include "threadpool.h"
21
22 /* POSIX thread function */
23 static void *
threadpool_worker(void * data)24 threadpool_worker(void *data)
25 {
26 struct threadpool *pool = data;
27
28 pthread_mutex_lock(&pool->m);
29
30 while (!pool->shutdown) {
31 struct threadpool_task *task;
32
33 /* Block (dropping the lock) until new work arrives for us. */
34 while (!pool->workqueue && !pool->shutdown)
35 pthread_cond_wait(&pool->new_work, &pool->m);
36
37 if (pool->shutdown)
38 break;
39
40 /* Pull the first task from the list. We don't free it -- it now lacks
41 * a reference other than the worker creator's, whose responsibility it
42 * is to call threadpool_wait_for_work() to free it.
43 */
44 task = pool->workqueue;
45 pool->workqueue = task->next;
46
47 /* Call the task's work func. */
48 pthread_mutex_unlock(&pool->m);
49 task->work(task->data);
50 pthread_mutex_lock(&pool->m);
51 task->finished = true;
52 pthread_cond_broadcast(&task->finish);
53 }
54
55 pthread_mutex_unlock(&pool->m);
56
57 return NULL;
58 }
59
60 /* Windows thread function */
61 static DWORD NINE_WINAPI
wthreadpool_worker(void * data)62 wthreadpool_worker(void *data)
63 {
64 threadpool_worker(data);
65
66 return 0;
67 }
68
69 struct threadpool *
_mesa_threadpool_create(struct NineSwapChain9 * swapchain)70 _mesa_threadpool_create(struct NineSwapChain9 *swapchain)
71 {
72 struct threadpool *pool = calloc(1, sizeof(*pool));
73
74 if (!pool)
75 return NULL;
76
77 pthread_mutex_init(&pool->m, NULL);
78 pthread_cond_init(&pool->new_work, NULL);
79
80 /* This uses WINE's CreateThread, so the thread function needs to use
81 * the Windows ABI */
82 pool->wthread = NineSwapChain9_CreateThread(swapchain, wthreadpool_worker, pool);
83 if (!pool->wthread) {
84 /* using pthread as fallback */
85 pthread_create(&pool->pthread, NULL, threadpool_worker, pool);
86 }
87 return pool;
88 }
89
90 void
_mesa_threadpool_destroy(struct NineSwapChain9 * swapchain,struct threadpool * pool)91 _mesa_threadpool_destroy(struct NineSwapChain9 *swapchain, struct threadpool *pool)
92 {
93 if (!pool)
94 return;
95
96 pthread_mutex_lock(&pool->m);
97 pool->shutdown = true;
98 pthread_cond_broadcast(&pool->new_work);
99 pthread_mutex_unlock(&pool->m);
100
101 if (pool->wthread) {
102 NineSwapChain9_WaitForThread(swapchain, pool->wthread);
103 } else {
104 pthread_join(pool->pthread, NULL);
105 }
106
107 pthread_cond_destroy(&pool->new_work);
108 pthread_mutex_destroy(&pool->m);
109 free(pool);
110 }
111
112 /**
113 * Queues a request for the work function to be asynchronously executed by the
114 * thread pool.
115 *
116 * The work func will get the "data" argument as its parameter -- any
117 * communication between the caller and the work function will occur through
118 * that.
119 *
120 * If there is an error, the work function is called immediately and NULL is
121 * returned.
122 */
123 struct threadpool_task *
_mesa_threadpool_queue_task(struct threadpool * pool,threadpool_task_func work,void * data)124 _mesa_threadpool_queue_task(struct threadpool *pool,
125 threadpool_task_func work, void *data)
126 {
127 struct threadpool_task *task, *previous;
128
129 if (!pool) {
130 work(data);
131 return NULL;
132 }
133
134 task = calloc(1, sizeof(*task));
135 if (!task) {
136 work(data);
137 return NULL;
138 }
139
140 task->work = work;
141 task->data = data;
142 task->next = NULL;
143 pthread_cond_init(&task->finish, NULL);
144
145 pthread_mutex_lock(&pool->m);
146
147 if (!pool->workqueue) {
148 pool->workqueue = task;
149 } else {
150 previous = pool->workqueue;
151 while (previous && previous->next)
152 previous = previous->next;
153
154 previous->next = task;
155 }
156 pthread_cond_signal(&pool->new_work);
157 pthread_mutex_unlock(&pool->m);
158
159 return task;
160 }
161
162 /**
163 * Blocks on the completion of the given task and frees the task.
164 */
165 void
_mesa_threadpool_wait_for_task(struct threadpool * pool,struct threadpool_task ** task_handle)166 _mesa_threadpool_wait_for_task(struct threadpool *pool,
167 struct threadpool_task **task_handle)
168 {
169 struct threadpool_task *task = *task_handle;
170
171 if (!pool || !task)
172 return;
173
174 pthread_mutex_lock(&pool->m);
175 while (!task->finished)
176 pthread_cond_wait(&task->finish, &pool->m);
177 pthread_mutex_unlock(&pool->m);
178
179 pthread_cond_destroy(&task->finish);
180 free(task);
181 *task_handle = NULL;
182 }
183