1/************************************************************************** 2 * 3 * Copyright 1999-2006 Brian Paul 4 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 **************************************************************************/ 25 26 27/** 28 * @file 29 * 30 * Thread, mutex, condition variable, barrier, semaphore and 31 * thread-specific data functions. 32 */ 33 34 35#ifndef OS_THREAD_H_ 36#define OS_THREAD_H_ 37 38 39#include "pipe/p_compiler.h" 40#include "util/u_debug.h" /* for assert */ 41 42 43#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE) || defined(PIPE_OS_HAIKU) || defined(PIPE_OS_CYGWIN) 44 45#include <pthread.h> /* POSIX threads headers */ 46#include <stdio.h> /* for perror() */ 47#include <signal.h> 48 49 50/* pipe_thread 51 */ 52typedef pthread_t pipe_thread; 53 54#define PIPE_THREAD_ROUTINE( name, param ) \ 55 void *name( void *param ) 56 57static INLINE pipe_thread pipe_thread_create( void *(* routine)( void *), void *param ) 58{ 59 pipe_thread thread; 60 sigset_t saved_set, new_set; 61 int ret; 62 63 sigfillset(&new_set); 64 pthread_sigmask(SIG_SETMASK, &new_set, &saved_set); 65 ret = pthread_create( &thread, NULL, routine, param ); 66 pthread_sigmask(SIG_SETMASK, &saved_set, NULL); 67 if (ret) 68 return 0; 69 return thread; 70} 71 72static INLINE int pipe_thread_wait( pipe_thread thread ) 73{ 74 return pthread_join( thread, NULL ); 75} 76 77static INLINE int pipe_thread_destroy( pipe_thread thread ) 78{ 79 return pthread_detach( thread ); 80} 81 82 83/* pipe_mutex 84 */ 85typedef pthread_mutex_t pipe_mutex; 86 87#define pipe_static_mutex(mutex) \ 88 static pipe_mutex mutex = PTHREAD_MUTEX_INITIALIZER 89 90#define pipe_mutex_init(mutex) \ 91 (void) pthread_mutex_init(&(mutex), NULL) 92 93#define pipe_mutex_destroy(mutex) \ 94 pthread_mutex_destroy(&(mutex)) 95 96#define pipe_mutex_lock(mutex) \ 97 (void) pthread_mutex_lock(&(mutex)) 98 99#define pipe_mutex_unlock(mutex) \ 100 (void) pthread_mutex_unlock(&(mutex)) 101 102 103/* pipe_condvar 104 */ 105typedef pthread_cond_t pipe_condvar; 106 107#define pipe_static_condvar(mutex) \ 108 static pipe_condvar mutex = PTHREAD_COND_INITIALIZER 109 110#define pipe_condvar_init(cond) \ 111 pthread_cond_init(&(cond), NULL) 112 113#define pipe_condvar_destroy(cond) \ 114 pthread_cond_destroy(&(cond)) 115 116#define pipe_condvar_wait(cond, mutex) \ 117 pthread_cond_wait(&(cond), &(mutex)) 118 119#define pipe_condvar_signal(cond) \ 120 pthread_cond_signal(&(cond)) 121 122#define pipe_condvar_broadcast(cond) \ 123 pthread_cond_broadcast(&(cond)) 124 125 126 127#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER) 128 129#include <windows.h> 130 131/* pipe_thread 132 */ 133typedef HANDLE pipe_thread; 134 135#define PIPE_THREAD_ROUTINE( name, param ) \ 136 void * WINAPI name( void *param ) 137 138static INLINE pipe_thread pipe_thread_create( void *(WINAPI * routine)( void *), void *param ) 139{ 140 DWORD id; 141 return CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) routine, param, 0, &id ); 142} 143 144static INLINE int pipe_thread_wait( pipe_thread thread ) 145{ 146 if (WaitForSingleObject( thread, INFINITE ) == WAIT_OBJECT_0) 147 return 0; 148 return -1; 149} 150 151static INLINE int pipe_thread_destroy( pipe_thread thread ) 152{ 153 if (CloseHandle( thread )) 154 return 0; 155 return -1; 156} 157 158 159/* pipe_mutex 160 */ 161typedef CRITICAL_SECTION pipe_mutex; 162 163/* http://locklessinc.com/articles/pthreads_on_windows/ */ 164#define pipe_static_mutex(mutex) \ 165 static pipe_mutex mutex = {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0} 166 167#define pipe_mutex_init(mutex) \ 168 InitializeCriticalSection(&mutex) 169 170#define pipe_mutex_destroy(mutex) \ 171 DeleteCriticalSection(&mutex) 172 173#define pipe_mutex_lock(mutex) \ 174 EnterCriticalSection(&mutex) 175 176#define pipe_mutex_unlock(mutex) \ 177 LeaveCriticalSection(&mutex) 178 179/* TODO: Need a macro to declare "I don't care about WinXP compatibilty" */ 180#if 0 && defined (_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) 181/* CONDITION_VARIABLE is only available on newer versions of Windows 182 * (Server 2008/Vista or later). 183 * http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx 184 * 185 * pipe_condvar 186 */ 187typedef CONDITION_VARIABLE pipe_condvar; 188 189#define pipe_static_condvar(cond) \ 190 /*static*/ pipe_condvar cond = CONDITION_VARIABLE_INIT 191 192#define pipe_condvar_init(cond) \ 193 InitializeConditionVariable(&(cond)) 194 195#define pipe_condvar_destroy(cond) \ 196 (void) cond /* nothing to do */ 197 198#define pipe_condvar_wait(cond, mutex) \ 199 SleepConditionVariableCS(&(cond), &(mutex), INFINITE) 200 201#define pipe_condvar_signal(cond) \ 202 WakeConditionVariable(&(cond)) 203 204#define pipe_condvar_broadcast(cond) \ 205 WakeAllConditionVariable(&(cond)) 206 207#else /* need compatibility with pre-Vista Win32 */ 208 209/* pipe_condvar (XXX FIX THIS) 210 * See http://www.cs.wustl.edu/~schmidt/win32-cv-1.html 211 * for potential pitfalls in implementation. 212 */ 213typedef DWORD pipe_condvar; 214 215#define pipe_static_condvar(cond) \ 216 /*static*/ pipe_condvar cond = 1 217 218#define pipe_condvar_init(cond) \ 219 (void) (cond = 1) 220 221#define pipe_condvar_destroy(cond) \ 222 (void) cond 223 224/* Poor man's pthread_cond_wait(): 225 Just release the mutex and sleep for one millisecond. 226 The caller's while() loop does all the work. */ 227#define pipe_condvar_wait(cond, mutex) \ 228 do { pipe_mutex_unlock(mutex); \ 229 Sleep(cond); \ 230 pipe_mutex_lock(mutex); \ 231 } while (0) 232 233#define pipe_condvar_signal(cond) \ 234 (void) cond 235 236#define pipe_condvar_broadcast(cond) \ 237 (void) cond 238 239#endif /* pre-Vista win32 */ 240 241#else 242 243#include "os/os_time.h" 244 245/** Dummy definitions */ 246 247typedef unsigned pipe_thread; 248 249#define PIPE_THREAD_ROUTINE( name, param ) \ 250 void * name( void *param ) 251 252static INLINE pipe_thread pipe_thread_create( void *(* routine)( void *), void *param ) 253{ 254 return 0; 255} 256 257static INLINE int pipe_thread_wait( pipe_thread thread ) 258{ 259 return -1; 260} 261 262static INLINE int pipe_thread_destroy( pipe_thread thread ) 263{ 264 return -1; 265} 266 267typedef unsigned pipe_mutex; 268 269#define pipe_static_mutex(mutex) \ 270 static pipe_mutex mutex = 0 271 272#define pipe_mutex_init(mutex) \ 273 (void) mutex 274 275#define pipe_mutex_destroy(mutex) \ 276 (void) mutex 277 278#define pipe_mutex_lock(mutex) \ 279 (void) mutex 280 281#define pipe_mutex_unlock(mutex) \ 282 (void) mutex 283 284typedef int64_t pipe_condvar; 285 286#define pipe_static_condvar(condvar) \ 287 static pipe_condvar condvar = 1000 288 289#define pipe_condvar_init(condvar) \ 290 (void) (condvar = 1000) 291 292#define pipe_condvar_destroy(condvar) \ 293 (void) condvar 294 295/* Poor man's pthread_cond_wait(): 296 Just release the mutex and sleep for one millisecond. 297 The caller's while() loop does all the work. */ 298#define pipe_condvar_wait(condvar, mutex) \ 299 do { pipe_mutex_unlock(mutex); \ 300 os_time_sleep(condvar); \ 301 pipe_mutex_lock(mutex); \ 302 } while (0) 303 304#define pipe_condvar_signal(condvar) \ 305 (void) condvar 306 307#define pipe_condvar_broadcast(condvar) \ 308 (void) condvar 309 310 311#endif /* PIPE_OS_? */ 312 313 314/* 315 * pipe_barrier 316 */ 317 318#if (defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS)) && !defined(PIPE_OS_ANDROID) 319 320typedef pthread_barrier_t pipe_barrier; 321 322static INLINE void pipe_barrier_init(pipe_barrier *barrier, unsigned count) 323{ 324 pthread_barrier_init(barrier, NULL, count); 325} 326 327static INLINE void pipe_barrier_destroy(pipe_barrier *barrier) 328{ 329 pthread_barrier_destroy(barrier); 330} 331 332static INLINE void pipe_barrier_wait(pipe_barrier *barrier) 333{ 334 pthread_barrier_wait(barrier); 335} 336 337 338#else /* If the OS doesn't have its own, implement barriers using a mutex and a condvar */ 339 340typedef struct { 341 unsigned count; 342 unsigned waiters; 343 uint64_t sequence; 344 pipe_mutex mutex; 345 pipe_condvar condvar; 346} pipe_barrier; 347 348static INLINE void pipe_barrier_init(pipe_barrier *barrier, unsigned count) 349{ 350 barrier->count = count; 351 barrier->waiters = 0; 352 barrier->sequence = 0; 353 pipe_mutex_init(barrier->mutex); 354 pipe_condvar_init(barrier->condvar); 355} 356 357static INLINE void pipe_barrier_destroy(pipe_barrier *barrier) 358{ 359 assert(barrier->waiters == 0); 360 pipe_mutex_destroy(barrier->mutex); 361 pipe_condvar_destroy(barrier->condvar); 362} 363 364static INLINE void pipe_barrier_wait(pipe_barrier *barrier) 365{ 366 pipe_mutex_lock(barrier->mutex); 367 368 assert(barrier->waiters < barrier->count); 369 barrier->waiters++; 370 371 if (barrier->waiters < barrier->count) { 372 uint64_t sequence = barrier->sequence; 373 374 do { 375 pipe_condvar_wait(barrier->condvar, barrier->mutex); 376 } while (sequence == barrier->sequence); 377 } else { 378 barrier->waiters = 0; 379 barrier->sequence++; 380 pipe_condvar_broadcast(barrier->condvar); 381 } 382 383 pipe_mutex_unlock(barrier->mutex); 384} 385 386 387#endif 388 389 390/* 391 * Semaphores 392 */ 393 394typedef struct 395{ 396 pipe_mutex mutex; 397 pipe_condvar cond; 398 int counter; 399} pipe_semaphore; 400 401 402static INLINE void 403pipe_semaphore_init(pipe_semaphore *sema, int init_val) 404{ 405 pipe_mutex_init(sema->mutex); 406 pipe_condvar_init(sema->cond); 407 sema->counter = init_val; 408} 409 410static INLINE void 411pipe_semaphore_destroy(pipe_semaphore *sema) 412{ 413 pipe_mutex_destroy(sema->mutex); 414 pipe_condvar_destroy(sema->cond); 415} 416 417/** Signal/increment semaphore counter */ 418static INLINE void 419pipe_semaphore_signal(pipe_semaphore *sema) 420{ 421 pipe_mutex_lock(sema->mutex); 422 sema->counter++; 423 pipe_condvar_signal(sema->cond); 424 pipe_mutex_unlock(sema->mutex); 425} 426 427/** Wait for semaphore counter to be greater than zero */ 428static INLINE void 429pipe_semaphore_wait(pipe_semaphore *sema) 430{ 431 pipe_mutex_lock(sema->mutex); 432 while (sema->counter <= 0) { 433 pipe_condvar_wait(sema->cond, sema->mutex); 434 } 435 sema->counter--; 436 pipe_mutex_unlock(sema->mutex); 437} 438 439 440 441/* 442 * Thread-specific data. 443 */ 444 445typedef struct { 446#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE) || defined(PIPE_OS_HAIKU) || defined(PIPE_OS_CYGWIN) 447 pthread_key_t key; 448#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER) 449 DWORD key; 450#endif 451 int initMagic; 452} pipe_tsd; 453 454 455#define PIPE_TSD_INIT_MAGIC 0xff8adc98 456 457 458static INLINE void 459pipe_tsd_init(pipe_tsd *tsd) 460{ 461#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE) || defined(PIPE_OS_HAIKU) || defined(PIPE_OS_CYGWIN) 462 if (pthread_key_create(&tsd->key, NULL/*free*/) != 0) { 463 perror("pthread_key_create(): failed to allocate key for thread specific data"); 464 exit(-1); 465 } 466#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER) 467 assert(0); 468#endif 469 tsd->initMagic = PIPE_TSD_INIT_MAGIC; 470} 471 472static INLINE void * 473pipe_tsd_get(pipe_tsd *tsd) 474{ 475 if (tsd->initMagic != (int) PIPE_TSD_INIT_MAGIC) { 476 pipe_tsd_init(tsd); 477 } 478#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE) || defined(PIPE_OS_HAIKU) || defined(PIPE_OS_CYGWIN) 479 return pthread_getspecific(tsd->key); 480#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER) 481 assert(0); 482 return NULL; 483#else 484 assert(0); 485 return NULL; 486#endif 487} 488 489static INLINE void 490pipe_tsd_set(pipe_tsd *tsd, void *value) 491{ 492 if (tsd->initMagic != (int) PIPE_TSD_INIT_MAGIC) { 493 pipe_tsd_init(tsd); 494 } 495#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_APPLE) || defined(PIPE_OS_HAIKU) || defined(PIPE_OS_CYGWIN) 496 if (pthread_setspecific(tsd->key, value) != 0) { 497 perror("pthread_set_specific() failed"); 498 exit(-1); 499 } 500#elif defined(PIPE_SUBSYSTEM_WINDOWS_USER) 501 assert(0); 502#else 503 assert(0); 504#endif 505} 506 507 508 509#endif /* OS_THREAD_H_ */ 510