xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/nine/threadpool.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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