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