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