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