sna_threads.c revision 428d7b3d
1/* 2 * Copyright © 2013 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 FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 * Authors: 24 * Chris Wilson <chris@chris-wilson.co.uk> 25 * 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif 31 32#include "sna.h" 33 34#include <unistd.h> 35#include <pthread.h> 36#include <signal.h> 37 38#ifdef HAVE_VALGRIND 39#include <valgrind.h> 40static inline bool valgrind_active(void) { return RUNNING_ON_VALGRIND; } 41#else 42static inline bool valgrind_active(void) { return false; } 43#endif 44 45static int max_threads = -1; 46 47static struct thread { 48 pthread_t thread; 49 pthread_mutex_t mutex; 50 pthread_cond_t cond; 51 52 void (*func)(void *arg); 53 void *arg; 54} *threads; 55 56static void *__run__(void *arg) 57{ 58 struct thread *t = arg; 59 sigset_t signals; 60 61 /* Disable all signals in the slave threads as X uses them for IO */ 62 sigfillset(&signals); 63 sigdelset(&signals, SIGBUS); 64 sigdelset(&signals, SIGSEGV); 65 pthread_sigmask(SIG_SETMASK, &signals, NULL); 66 67 pthread_mutex_lock(&t->mutex); 68 while (1) { 69 while (t->func == NULL) 70 pthread_cond_wait(&t->cond, &t->mutex); 71 pthread_mutex_unlock(&t->mutex); 72 73 assert(t->func); 74 t->func(t->arg); 75 76 pthread_mutex_lock(&t->mutex); 77 t->arg = NULL; 78 t->func = NULL; 79 pthread_cond_signal(&t->cond); 80 } 81 pthread_mutex_unlock(&t->mutex); 82 83 return NULL; 84} 85 86#if defined(__GNUC__) 87#define popcount(x) __builtin_popcount(x) 88#else 89static int popcount(unsigned int x) 90{ 91 int count = 0; 92 93 while (x) { 94 count += x&1; 95 x >>= 1; 96 } 97 98 return count; 99} 100#endif 101 102static int 103num_cores(void) 104{ 105 FILE *file = fopen("/proc/cpuinfo", "r"); 106 int count = 0; 107 if (file) { 108 size_t len = 0; 109 char *line = NULL; 110 uint32_t processors = 0, cores = 0; 111 while (getline(&line, &len, file) != -1) { 112 int id; 113 if (sscanf(line, "physical id : %d", &id) == 1) { 114 if (id >= 32) 115 continue; 116 processors |= 1 << id; 117 } else if (sscanf(line, "core id : %d", &id) == 1) { 118 if (id >= 32) 119 continue; 120 cores |= 1 << id; 121 } 122 } 123 free(line); 124 fclose(file); 125 126 DBG(("%s: processors=0x%08x, cores=0x%08x\n", 127 __FUNCTION__, processors, cores)); 128 129 count = popcount(processors) * popcount(cores); 130 } 131 return count; 132} 133 134void sna_threads_init(void) 135{ 136 int n; 137 138 if (max_threads != -1) 139 return; 140 141 if (valgrind_active()) 142 goto bail; 143 144 max_threads = num_cores(); 145 if (max_threads == 0) 146 max_threads = sysconf(_SC_NPROCESSORS_ONLN) / 2; 147 if (max_threads <= 1) 148 goto bail; 149 150 DBG(("%s: creating a thread pool of %d threads\n", 151 __func__, max_threads)); 152 153 threads = malloc (sizeof(threads[0])*max_threads); 154 if (threads == NULL) 155 goto bail; 156 157 for (n = 1; n < max_threads; n++) { 158 pthread_mutex_init(&threads[n].mutex, NULL); 159 pthread_cond_init(&threads[n].cond, NULL); 160 161 threads[n].func = NULL; 162 threads[n].arg = NULL; 163 if (pthread_create(&threads[n].thread, NULL, 164 __run__, &threads[n])) 165 goto bail; 166 } 167 168 threads[0].thread = pthread_self(); 169 return; 170 171bail: 172 max_threads = 0; 173} 174 175void sna_threads_run(int id, void (*func)(void *arg), void *arg) 176{ 177 assert(max_threads > 0); 178 assert(pthread_self() == threads[0].thread); 179 assert(id > 0 && id < max_threads); 180 181 assert(threads[id].func == NULL); 182 183 pthread_mutex_lock(&threads[id].mutex); 184 threads[id].func = func; 185 threads[id].arg = arg; 186 pthread_cond_signal(&threads[id].cond); 187 pthread_mutex_unlock(&threads[id].mutex); 188} 189 190void sna_threads_trap(int sig) 191{ 192 pthread_t t = pthread_self(); 193 int n; 194 195 if (max_threads == 0) 196 return; 197 198 if (t == threads[0].thread) 199 return; 200 201 for (n = 1; threads[n].thread != t; n++) 202 ; 203 204 ERR(("%s: thread[%d] caught signal %d\n", __func__, n, sig)); 205 206 pthread_mutex_lock(&threads[n].mutex); 207 threads[n].arg = (void *)(intptr_t)sig; 208 threads[n].func = NULL; 209 pthread_cond_signal(&threads[n].cond); 210 pthread_mutex_unlock(&threads[n].mutex); 211 212 pthread_exit(&sig); 213} 214 215void sna_threads_wait(void) 216{ 217 int n; 218 219 assert(max_threads > 0); 220 assert(pthread_self() == threads[0].thread); 221 222 for (n = 1; n < max_threads; n++) { 223 if (threads[n].func != NULL) { 224 pthread_mutex_lock(&threads[n].mutex); 225 while (threads[n].func) 226 pthread_cond_wait(&threads[n].cond, &threads[n].mutex); 227 pthread_mutex_unlock(&threads[n].mutex); 228 } 229 230 if (threads[n].arg != NULL) { 231 DBG(("%s: thread[%d] died from signal %d\n", __func__, n, (int)(intptr_t)threads[n].arg)); 232 sna_threads_kill(); 233 return; 234 } 235 } 236} 237 238void sna_threads_kill(void) 239{ 240 int n; 241 242 ERR(("%s: kill %d threads\n", __func__, max_threads)); 243 assert(max_threads > 0); 244 assert(pthread_self() == threads[0].thread); 245 246 for (n = 1; n < max_threads; n++) 247 pthread_cancel(threads[n].thread); 248 249 for (n = 1; n < max_threads; n++) 250 pthread_join(threads[n].thread, NULL); 251 252 max_threads = 0; 253} 254 255int sna_use_threads(int width, int height, int threshold) 256{ 257 int num_threads; 258 259 if (max_threads <= 0) 260 return 1; 261 262 if (height <= 1) 263 return 1; 264 265 if (width < 128) 266 height /= 128/width; 267 268 num_threads = height * max_threads / threshold - 1; 269 if (num_threads <= 0) 270 return 1; 271 272 if (num_threads > max_threads) 273 num_threads = max_threads; 274 if (num_threads > height) 275 num_threads = height; 276 277 return num_threads; 278} 279 280struct thread_composite { 281 pixman_image_t *src, *mask, *dst; 282 pixman_op_t op; 283 int16_t src_x, src_y; 284 int16_t mask_x, mask_y; 285 int16_t dst_x, dst_y; 286 uint16_t width, height; 287}; 288 289static void thread_composite(void *arg) 290{ 291 struct thread_composite *t = arg; 292 pixman_image_composite(t->op, t->src, t->mask, t->dst, 293 t->src_x, t->src_y, 294 t->mask_x, t->mask_y, 295 t->dst_x, t->dst_y, 296 t->width, t->height); 297} 298 299void sna_image_composite(pixman_op_t op, 300 pixman_image_t *src, 301 pixman_image_t *mask, 302 pixman_image_t *dst, 303 int16_t src_x, 304 int16_t src_y, 305 int16_t mask_x, 306 int16_t mask_y, 307 int16_t dst_x, 308 int16_t dst_y, 309 uint16_t width, 310 uint16_t height) 311{ 312 int num_threads; 313 314 num_threads = sna_use_threads(width, height, 32); 315 if (num_threads <= 1) { 316 if (sigtrap_get() == 0) { 317 pixman_image_composite(op, src, mask, dst, 318 src_x, src_y, 319 mask_x, mask_y, 320 dst_x, dst_y, 321 width, height); 322 sigtrap_put(); 323 } 324 } else { 325 struct thread_composite data[num_threads]; 326 int y, dy, n; 327 328 DBG(("%s: using %d threads for compositing %dx%d\n", 329 __FUNCTION__, num_threads, width, height)); 330 331 y = dst_y; 332 dy = (height + num_threads - 1) / num_threads; 333 num_threads -= (num_threads-1) * dy >= height; 334 335 data[0].op = op; 336 data[0].src = src; 337 data[0].mask = mask; 338 data[0].dst = dst; 339 data[0].src_x = src_x; 340 data[0].src_y = src_y; 341 data[0].mask_x = mask_x; 342 data[0].mask_y = mask_y; 343 data[0].dst_x = dst_x; 344 data[0].dst_y = y; 345 data[0].width = width; 346 data[0].height = dy; 347 348 if (sigtrap_get() == 0) { 349 for (n = 1; n < num_threads; n++) { 350 data[n] = data[0]; 351 data[n].src_y += y - dst_y; 352 data[n].mask_y += y - dst_y; 353 data[n].dst_y = y; 354 y += dy; 355 356 sna_threads_run(n, thread_composite, &data[n]); 357 } 358 359 assert(y < dst_y + height); 360 if (y + dy > dst_y + height) 361 dy = dst_y + height - y; 362 363 data[0].src_y += y - dst_y; 364 data[0].mask_y += y - dst_y; 365 data[0].dst_y = y; 366 data[0].height = dy; 367 368 thread_composite(&data[0]); 369 370 sna_threads_wait(); 371 sigtrap_put(); 372 } else 373 sna_threads_kill(); 374 } 375} 376