1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "swapchain9.h"
25 #include "surface9.h"
26 #include "device9.h"
27
28 #include "nine_helpers.h"
29 #include "nine_pipe.h"
30 #include "nine_dump.h"
31
32 #include "util/u_inlines.h"
33 #include "util/u_surface.h"
34 #include "hud/hud_context.h"
35 #include "frontend/drm_driver.h"
36
37 #include "os/os_thread.h"
38 #include "threadpool.h"
39
40 /* POSIX thread function */
41 static void *
threadpool_worker(void * data)42 threadpool_worker(void *data)
43 {
44 struct threadpool *pool = data;
45
46 pthread_mutex_lock(&pool->m);
47
48 while (!pool->shutdown) {
49 struct threadpool_task *task;
50
51 /* Block (dropping the lock) until new work arrives for us. */
52 while (!pool->workqueue && !pool->shutdown)
53 pthread_cond_wait(&pool->new_work, &pool->m);
54
55 if (pool->shutdown)
56 break;
57
58 /* Pull the first task from the list. We don't free it -- it now lacks
59 * a reference other than the worker creator's, whose responsibility it
60 * is to call threadpool_wait_for_work() to free it.
61 */
62 task = pool->workqueue;
63 pool->workqueue = task->next;
64
65 /* Call the task's work func. */
66 pthread_mutex_unlock(&pool->m);
67 task->work(task->data);
68 pthread_mutex_lock(&pool->m);
69 task->finished = TRUE;
70 pthread_cond_broadcast(&task->finish);
71 }
72
73 pthread_mutex_unlock(&pool->m);
74
75 return NULL;
76 }
77
78 /* Windows thread function */
79 static DWORD NINE_WINAPI
wthreadpool_worker(void * data)80 wthreadpool_worker(void *data)
81 {
82 threadpool_worker(data);
83
84 return 0;
85 }
86
87 struct threadpool *
_mesa_threadpool_create(struct NineSwapChain9 * swapchain)88 _mesa_threadpool_create(struct NineSwapChain9 *swapchain)
89 {
90 struct threadpool *pool = calloc(1, sizeof(*pool));
91
92 if (!pool)
93 return NULL;
94
95 pthread_mutex_init(&pool->m, NULL);
96 pthread_cond_init(&pool->new_work, NULL);
97
98 /* This uses WINE's CreateThread, so the thread function needs to use
99 * the Windows ABI */
100 pool->wthread = NineSwapChain9_CreateThread(swapchain, wthreadpool_worker, pool);
101 if (!pool->wthread) {
102 /* using pthread as fallback */
103 pthread_create(&pool->pthread, NULL, threadpool_worker, pool);
104 }
105 return pool;
106 }
107
108 void
_mesa_threadpool_destroy(struct NineSwapChain9 * swapchain,struct threadpool * pool)109 _mesa_threadpool_destroy(struct NineSwapChain9 *swapchain, struct threadpool *pool)
110 {
111 if (!pool)
112 return;
113
114 pthread_mutex_lock(&pool->m);
115 pool->shutdown = TRUE;
116 pthread_cond_broadcast(&pool->new_work);
117 pthread_mutex_unlock(&pool->m);
118
119 if (pool->wthread) {
120 NineSwapChain9_WaitForThread(swapchain, pool->wthread);
121 } else {
122 pthread_join(pool->pthread, NULL);
123 }
124
125 pthread_cond_destroy(&pool->new_work);
126 pthread_mutex_destroy(&pool->m);
127 free(pool);
128 }
129
130 /**
131 * Queues a request for the work function to be asynchronously executed by the
132 * thread pool.
133 *
134 * The work func will get the "data" argument as its parameter -- any
135 * communication between the caller and the work function will occur through
136 * that.
137 *
138 * If there is an error, the work function is called immediately and NULL is
139 * returned.
140 */
141 struct threadpool_task *
_mesa_threadpool_queue_task(struct threadpool * pool,threadpool_task_func work,void * data)142 _mesa_threadpool_queue_task(struct threadpool *pool,
143 threadpool_task_func work, void *data)
144 {
145 struct threadpool_task *task, *previous;
146
147 if (!pool) {
148 work(data);
149 return NULL;
150 }
151
152 task = calloc(1, sizeof(*task));
153 if (!task) {
154 work(data);
155 return NULL;
156 }
157
158 task->work = work;
159 task->data = data;
160 task->next = NULL;
161 pthread_cond_init(&task->finish, NULL);
162
163 pthread_mutex_lock(&pool->m);
164
165 if (!pool->workqueue) {
166 pool->workqueue = task;
167 } else {
168 previous = pool->workqueue;
169 while (previous && previous->next)
170 previous = previous->next;
171
172 previous->next = task;
173 }
174 pthread_cond_signal(&pool->new_work);
175 pthread_mutex_unlock(&pool->m);
176
177 return task;
178 }
179
180 /**
181 * Blocks on the completion of the given task and frees the task.
182 */
183 void
_mesa_threadpool_wait_for_task(struct threadpool * pool,struct threadpool_task ** task_handle)184 _mesa_threadpool_wait_for_task(struct threadpool *pool,
185 struct threadpool_task **task_handle)
186 {
187 struct threadpool_task *task = *task_handle;
188
189 if (!pool || !task)
190 return;
191
192 pthread_mutex_lock(&pool->m);
193 while (!task->finished)
194 pthread_cond_wait(&task->finish, &pool->m);
195 pthread_mutex_unlock(&pool->m);
196
197 pthread_cond_destroy(&task->finish);
198 free(task);
199 *task_handle = NULL;
200 }
201