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 */
41static void *
42threadpool_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 */
79static DWORD NINE_WINAPI
80wthreadpool_worker(void *data)
81{
82    threadpool_worker(data);
83
84    return 0;
85}
86
87struct threadpool *
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
108void
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 */
141struct threadpool_task *
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 */
183void
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