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 */ 52 typedef pthread_t pipe_thread; 53 54 #define PIPE_THREAD_ROUTINE( name, param ) \ 55 void *name( void *param ) 56 57 static 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 72 static INLINE int pipe_thread_wait( pipe_thread thread ) 73 { 74 return pthread_join( thread, NULL ); 75 } 76 77 static INLINE int pipe_thread_destroy( pipe_thread thread ) 78 { 79 return pthread_detach( thread ); 80 } 81 82 83 /* pipe_mutex 84 */ 85 typedef 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 */ 105 typedef 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 */ 133 typedef HANDLE pipe_thread; 134 135 #define PIPE_THREAD_ROUTINE( name, param ) \ 136 void * WINAPI name( void *param ) 137 138 static 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 144 static 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 151 static 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 */ 161 typedef 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 */ 187 typedef 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 */ 213 typedef 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 247 typedef unsigned pipe_thread; 248 249 #define PIPE_THREAD_ROUTINE( name, param ) \ 250 void * name( void *param ) 251 252 static INLINE pipe_thread pipe_thread_create( void *(* routine)( void *), void *param ) 253 { 254 return 0; 255 } 256 257 static INLINE int pipe_thread_wait( pipe_thread thread ) 258 { 259 return -1; 260 } 261 262 static INLINE int pipe_thread_destroy( pipe_thread thread ) 263 { 264 return -1; 265 } 266 267 typedef 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 284 typedef 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 320 typedef pthread_barrier_t pipe_barrier; 321 322 static INLINE void pipe_barrier_init(pipe_barrier *barrier, unsigned count) 323 { 324 pthread_barrier_init(barrier, NULL, count); 325 } 326 327 static INLINE void pipe_barrier_destroy(pipe_barrier *barrier) 328 { 329 pthread_barrier_destroy(barrier); 330 } 331 332 static 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 340 typedef struct { 341 unsigned count; 342 unsigned waiters; 343 uint64_t sequence; 344 pipe_mutex mutex; 345 pipe_condvar condvar; 346 } pipe_barrier; 347 348 static 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 357 static 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 364 static 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 394 typedef struct 395 { 396 pipe_mutex mutex; 397 pipe_condvar cond; 398 int counter; 399 } pipe_semaphore; 400 401 402 static INLINE void 403 pipe_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 410 static INLINE void 411 pipe_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 */ 418 static INLINE void 419 pipe_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 */ 428 static INLINE void 429 pipe_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 445 typedef 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 458 static INLINE void 459 pipe_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 472 static INLINE void * 473 pipe_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 489 static INLINE void 490 pipe_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