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 "state_tracker/drm_driver.h"
36 
37 #include "os/os_thread.h"
38 #include "threadpool.h"
39 
40 static void *
41 threadpool_worker(void *data)
42 {
43     struct threadpool *pool = data;
44 
45     pthread_mutex_lock(&pool->m);
46 
47     while (!pool->shutdown) {
48         struct threadpool_task *task;
49 
50         /* Block (dropping the lock) until new work arrives for us. */
51         while (!pool->workqueue && !pool->shutdown)
52             pthread_cond_wait(&pool->new_work, &pool->m);
53 
54         if (pool->shutdown) {
55             pthread_mutex_unlock(&pool->m);
56             return NULL;
57         }
58 
59         /* Pull the first task from the list.  We don't free it -- it now lacks
60          * a reference other than the worker creator's, whose responsibility it
61          * is to call threadpool_wait_for_work() to free it.
62          */
63         task = pool->workqueue;
64         pool->workqueue = task->next;
65 
66         /* Call the task's work func. */
67         pthread_mutex_unlock(&pool->m);
68         task->work(task->data);
69         pthread_mutex_lock(&pool->m);
70         task->finished = TRUE;
71         pthread_cond_broadcast(&task->finish);
72     }
73 
74     pthread_mutex_unlock(&pool->m);
75 
76     return NULL;
77 }
78 
79 struct threadpool *
80 _mesa_threadpool_create(struct NineSwapChain9 *swapchain)
81 {
82     struct threadpool *pool = calloc(1, sizeof(*pool));
83 
84     if (!pool)
85         return NULL;
86 
87     pthread_mutex_init(&pool->m, NULL);
88     pthread_cond_init(&pool->new_work, NULL);
89 
90     pool->wthread = NineSwapChain9_CreateThread(swapchain, threadpool_worker, pool);
91     if (!pool->wthread) {
92         /* using pthread as fallback */
93         pthread_create(&pool->pthread, NULL, threadpool_worker, pool);
94     }
95     return pool;
96 }
97 
98 void
99 _mesa_threadpool_destroy(struct NineSwapChain9 *swapchain, struct threadpool *pool)
100 {
101     if (!pool)
102         return;
103 
104     pthread_mutex_lock(&pool->m);
105     pool->shutdown = TRUE;
106     pthread_cond_broadcast(&pool->new_work);
107     pthread_mutex_unlock(&pool->m);
108 
109     if (pool->wthread) {
110         NineSwapChain9_WaitForThread(swapchain, pool->wthread);
111     } else {
112         pthread_join(pool->pthread, NULL);
113     }
114 
115     pthread_cond_destroy(&pool->new_work);
116     pthread_mutex_destroy(&pool->m);
117     free(pool);
118 }
119 
120 /**
121  * Queues a request for the work function to be asynchronously executed by the
122  * thread pool.
123  *
124  * The work func will get the "data" argument as its parameter -- any
125  * communication between the caller and the work function will occur through
126  * that.
127  *
128  * If there is an error, the work function is called immediately and NULL is
129  * returned.
130  */
131 struct threadpool_task *
132 _mesa_threadpool_queue_task(struct threadpool *pool,
133                             threadpool_task_func work, void *data)
134 {
135     struct threadpool_task *task, *previous;
136 
137     if (!pool) {
138         work(data);
139         return NULL;
140     }
141 
142     task = calloc(1, sizeof(*task));
143     if (!task) {
144         work(data);
145         return NULL;
146     }
147 
148     task->work = work;
149     task->data = data;
150     task->next = NULL;
151     pthread_cond_init(&task->finish, NULL);
152 
153     pthread_mutex_lock(&pool->m);
154 
155     if (!pool->workqueue) {
156         pool->workqueue = task;
157     } else {
158         previous = pool->workqueue;
159         while (previous && previous->next)
160             previous = previous->next;
161 
162         previous->next = task;
163     }
164     pthread_cond_signal(&pool->new_work);
165     pthread_mutex_unlock(&pool->m);
166 
167     return task;
168 }
169 
170 /**
171  * Blocks on the completion of the given task and frees the task.
172  */
173 void
174 _mesa_threadpool_wait_for_task(struct threadpool *pool,
175                                struct threadpool_task **task_handle)
176 {
177     struct threadpool_task *task = *task_handle;
178 
179     if (!pool || !task)
180         return;
181 
182     pthread_mutex_lock(&pool->m);
183     while (!task->finished)
184         pthread_cond_wait(&task->finish, &pool->m);
185     pthread_mutex_unlock(&pool->m);
186 
187     pthread_cond_destroy(&task->finish);
188     free(task);
189     *task_handle = NULL;
190 }
191