tinycthread.c (13065B)
1 /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- 2 Copyright (c) 2012 Marcus Geelnard 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not 13 claim that you wrote the original software. If you use this software 14 in a product, an acknowledgment in the product documentation would be 15 appreciated but is not required. 16 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 20 3. This notice may not be removed or altered from any source 21 distribution. 22 */ 23 24 /* 2013-01-06 Camilla Löwy <elmindreda@glfw.org> 25 * 26 * Added casts from time_t to DWORD to avoid warnings on VC++. 27 * Fixed time retrieval on POSIX systems. 28 */ 29 30 #include "tinycthread.h" 31 #include <stdlib.h> 32 33 /* Platform specific includes */ 34 #if defined(_TTHREAD_POSIX_) 35 #include <signal.h> 36 #include <sched.h> 37 #include <unistd.h> 38 #include <sys/time.h> 39 #include <errno.h> 40 #elif defined(_TTHREAD_WIN32_) 41 #include <process.h> 42 #include <sys/timeb.h> 43 #endif 44 45 /* Standard, good-to-have defines */ 46 #ifndef NULL 47 #define NULL (void*)0 48 #endif 49 #ifndef TRUE 50 #define TRUE 1 51 #endif 52 #ifndef FALSE 53 #define FALSE 0 54 #endif 55 56 int mtx_init(mtx_t *mtx, int type) 57 { 58 #if defined(_TTHREAD_WIN32_) 59 mtx->mAlreadyLocked = FALSE; 60 mtx->mRecursive = type & mtx_recursive; 61 InitializeCriticalSection(&mtx->mHandle); 62 return thrd_success; 63 #else 64 int ret; 65 pthread_mutexattr_t attr; 66 pthread_mutexattr_init(&attr); 67 if (type & mtx_recursive) 68 { 69 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 70 } 71 ret = pthread_mutex_init(mtx, &attr); 72 pthread_mutexattr_destroy(&attr); 73 return ret == 0 ? thrd_success : thrd_error; 74 #endif 75 } 76 77 void mtx_destroy(mtx_t *mtx) 78 { 79 #if defined(_TTHREAD_WIN32_) 80 DeleteCriticalSection(&mtx->mHandle); 81 #else 82 pthread_mutex_destroy(mtx); 83 #endif 84 } 85 86 int mtx_lock(mtx_t *mtx) 87 { 88 #if defined(_TTHREAD_WIN32_) 89 EnterCriticalSection(&mtx->mHandle); 90 if (!mtx->mRecursive) 91 { 92 while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */ 93 mtx->mAlreadyLocked = TRUE; 94 } 95 return thrd_success; 96 #else 97 return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error; 98 #endif 99 } 100 101 int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) 102 { 103 /* FIXME! */ 104 (void)mtx; 105 (void)ts; 106 return thrd_error; 107 } 108 109 int mtx_trylock(mtx_t *mtx) 110 { 111 #if defined(_TTHREAD_WIN32_) 112 int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy; 113 if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked) 114 { 115 LeaveCriticalSection(&mtx->mHandle); 116 ret = thrd_busy; 117 } 118 return ret; 119 #else 120 return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; 121 #endif 122 } 123 124 int mtx_unlock(mtx_t *mtx) 125 { 126 #if defined(_TTHREAD_WIN32_) 127 mtx->mAlreadyLocked = FALSE; 128 LeaveCriticalSection(&mtx->mHandle); 129 return thrd_success; 130 #else 131 return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;; 132 #endif 133 } 134 135 #if defined(_TTHREAD_WIN32_) 136 #define _CONDITION_EVENT_ONE 0 137 #define _CONDITION_EVENT_ALL 1 138 #endif 139 140 int cnd_init(cnd_t *cond) 141 { 142 #if defined(_TTHREAD_WIN32_) 143 cond->mWaitersCount = 0; 144 145 /* Init critical section */ 146 InitializeCriticalSection(&cond->mWaitersCountLock); 147 148 /* Init events */ 149 cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); 150 if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL) 151 { 152 cond->mEvents[_CONDITION_EVENT_ALL] = NULL; 153 return thrd_error; 154 } 155 cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); 156 if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL) 157 { 158 CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); 159 cond->mEvents[_CONDITION_EVENT_ONE] = NULL; 160 return thrd_error; 161 } 162 163 return thrd_success; 164 #else 165 return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error; 166 #endif 167 } 168 169 void cnd_destroy(cnd_t *cond) 170 { 171 #if defined(_TTHREAD_WIN32_) 172 if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL) 173 { 174 CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); 175 } 176 if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL) 177 { 178 CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]); 179 } 180 DeleteCriticalSection(&cond->mWaitersCountLock); 181 #else 182 pthread_cond_destroy(cond); 183 #endif 184 } 185 186 int cnd_signal(cnd_t *cond) 187 { 188 #if defined(_TTHREAD_WIN32_) 189 int haveWaiters; 190 191 /* Are there any waiters? */ 192 EnterCriticalSection(&cond->mWaitersCountLock); 193 haveWaiters = (cond->mWaitersCount > 0); 194 LeaveCriticalSection(&cond->mWaitersCountLock); 195 196 /* If we have any waiting threads, send them a signal */ 197 if(haveWaiters) 198 { 199 if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0) 200 { 201 return thrd_error; 202 } 203 } 204 205 return thrd_success; 206 #else 207 return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; 208 #endif 209 } 210 211 int cnd_broadcast(cnd_t *cond) 212 { 213 #if defined(_TTHREAD_WIN32_) 214 int haveWaiters; 215 216 /* Are there any waiters? */ 217 EnterCriticalSection(&cond->mWaitersCountLock); 218 haveWaiters = (cond->mWaitersCount > 0); 219 LeaveCriticalSection(&cond->mWaitersCountLock); 220 221 /* If we have any waiting threads, send them a signal */ 222 if(haveWaiters) 223 { 224 if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) 225 { 226 return thrd_error; 227 } 228 } 229 230 return thrd_success; 231 #else 232 return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; 233 #endif 234 } 235 236 #if defined(_TTHREAD_WIN32_) 237 static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout) 238 { 239 int result, lastWaiter; 240 241 /* Increment number of waiters */ 242 EnterCriticalSection(&cond->mWaitersCountLock); 243 ++ cond->mWaitersCount; 244 LeaveCriticalSection(&cond->mWaitersCountLock); 245 246 /* Release the mutex while waiting for the condition (will decrease 247 the number of waiters when done)... */ 248 mtx_unlock(mtx); 249 250 /* Wait for either event to become signaled due to cnd_signal() or 251 cnd_broadcast() being called */ 252 result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout); 253 if (result == WAIT_TIMEOUT) 254 { 255 return thrd_timeout; 256 } 257 else if (result == (int)WAIT_FAILED) 258 { 259 return thrd_error; 260 } 261 262 /* Check if we are the last waiter */ 263 EnterCriticalSection(&cond->mWaitersCountLock); 264 -- cond->mWaitersCount; 265 lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && 266 (cond->mWaitersCount == 0); 267 LeaveCriticalSection(&cond->mWaitersCountLock); 268 269 /* If we are the last waiter to be notified to stop waiting, reset the event */ 270 if (lastWaiter) 271 { 272 if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) 273 { 274 return thrd_error; 275 } 276 } 277 278 /* Re-acquire the mutex */ 279 mtx_lock(mtx); 280 281 return thrd_success; 282 } 283 #endif 284 285 int cnd_wait(cnd_t *cond, mtx_t *mtx) 286 { 287 #if defined(_TTHREAD_WIN32_) 288 return _cnd_timedwait_win32(cond, mtx, INFINITE); 289 #else 290 return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; 291 #endif 292 } 293 294 int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) 295 { 296 #if defined(_TTHREAD_WIN32_) 297 struct timespec now; 298 if (clock_gettime(CLOCK_REALTIME, &now) == 0) 299 { 300 DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 + 301 (ts->tv_nsec - now.tv_nsec + 500000) / 1000000); 302 return _cnd_timedwait_win32(cond, mtx, delta); 303 } 304 else 305 return thrd_error; 306 #else 307 int ret; 308 ret = pthread_cond_timedwait(cond, mtx, ts); 309 if (ret == ETIMEDOUT) 310 { 311 return thrd_timeout; 312 } 313 return ret == 0 ? thrd_success : thrd_error; 314 #endif 315 } 316 317 318 /** Information to pass to the new thread (what to run). */ 319 typedef struct { 320 thrd_start_t mFunction; /**< Pointer to the function to be executed. */ 321 void * mArg; /**< Function argument for the thread function. */ 322 } _thread_start_info; 323 324 /* Thread wrapper function. */ 325 #if defined(_TTHREAD_WIN32_) 326 static unsigned WINAPI _thrd_wrapper_function(void * aArg) 327 #elif defined(_TTHREAD_POSIX_) 328 static void * _thrd_wrapper_function(void * aArg) 329 #endif 330 { 331 thrd_start_t fun; 332 void *arg; 333 int res; 334 #if defined(_TTHREAD_POSIX_) 335 void *pres; 336 #endif 337 338 /* Get thread startup information */ 339 _thread_start_info *ti = (_thread_start_info *) aArg; 340 fun = ti->mFunction; 341 arg = ti->mArg; 342 343 /* The thread is responsible for freeing the startup information */ 344 free((void *)ti); 345 346 /* Call the actual client thread function */ 347 res = fun(arg); 348 349 #if defined(_TTHREAD_WIN32_) 350 return res; 351 #else 352 pres = malloc(sizeof(int)); 353 if (pres != NULL) 354 { 355 *(int*)pres = res; 356 } 357 return pres; 358 #endif 359 } 360 361 int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 362 { 363 /* Fill out the thread startup information (passed to the thread wrapper, 364 which will eventually free it) */ 365 _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info)); 366 if (ti == NULL) 367 { 368 return thrd_nomem; 369 } 370 ti->mFunction = func; 371 ti->mArg = arg; 372 373 /* Create the thread */ 374 #if defined(_TTHREAD_WIN32_) 375 *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL); 376 #elif defined(_TTHREAD_POSIX_) 377 if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0) 378 { 379 *thr = 0; 380 } 381 #endif 382 383 /* Did we fail to create the thread? */ 384 if(!*thr) 385 { 386 free(ti); 387 return thrd_error; 388 } 389 390 return thrd_success; 391 } 392 393 thrd_t thrd_current(void) 394 { 395 #if defined(_TTHREAD_WIN32_) 396 return GetCurrentThread(); 397 #else 398 return pthread_self(); 399 #endif 400 } 401 402 int thrd_detach(thrd_t thr) 403 { 404 /* FIXME! */ 405 (void)thr; 406 return thrd_error; 407 } 408 409 int thrd_equal(thrd_t thr0, thrd_t thr1) 410 { 411 #if defined(_TTHREAD_WIN32_) 412 return thr0 == thr1; 413 #else 414 return pthread_equal(thr0, thr1); 415 #endif 416 } 417 418 void thrd_exit(int res) 419 { 420 #if defined(_TTHREAD_WIN32_) 421 ExitThread(res); 422 #else 423 void *pres = malloc(sizeof(int)); 424 if (pres != NULL) 425 { 426 *(int*)pres = res; 427 } 428 pthread_exit(pres); 429 #endif 430 } 431 432 int thrd_join(thrd_t thr, int *res) 433 { 434 #if defined(_TTHREAD_WIN32_) 435 if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED) 436 { 437 return thrd_error; 438 } 439 if (res != NULL) 440 { 441 DWORD dwRes; 442 GetExitCodeThread(thr, &dwRes); 443 *res = dwRes; 444 } 445 #elif defined(_TTHREAD_POSIX_) 446 void *pres; 447 int ires = 0; 448 if (pthread_join(thr, &pres) != 0) 449 { 450 return thrd_error; 451 } 452 if (pres != NULL) 453 { 454 ires = *(int*)pres; 455 free(pres); 456 } 457 if (res != NULL) 458 { 459 *res = ires; 460 } 461 #endif 462 return thrd_success; 463 } 464 465 int thrd_sleep(const struct timespec *time_point, struct timespec *remaining) 466 { 467 struct timespec now; 468 #if defined(_TTHREAD_WIN32_) 469 DWORD delta; 470 #else 471 long delta; 472 #endif 473 474 /* Get the current time */ 475 if (clock_gettime(CLOCK_REALTIME, &now) != 0) 476 return -2; // FIXME: Some specific error code? 477 478 #if defined(_TTHREAD_WIN32_) 479 /* Delta in milliseconds */ 480 delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 + 481 (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000); 482 if (delta > 0) 483 { 484 Sleep(delta); 485 } 486 #else 487 /* Delta in microseconds */ 488 delta = (time_point->tv_sec - now.tv_sec) * 1000000L + 489 (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L; 490 491 /* On some systems, the usleep argument must be < 1000000 */ 492 while (delta > 999999L) 493 { 494 usleep(999999); 495 delta -= 999999L; 496 } 497 if (delta > 0L) 498 { 499 usleep((useconds_t)delta); 500 } 501 #endif 502 503 /* We don't support waking up prematurely (yet) */ 504 if (remaining) 505 { 506 remaining->tv_sec = 0; 507 remaining->tv_nsec = 0; 508 } 509 return 0; 510 } 511 512 void thrd_yield(void) 513 { 514 #if defined(_TTHREAD_WIN32_) 515 Sleep(0); 516 #else 517 sched_yield(); 518 #endif 519 } 520 521 int tss_create(tss_t *key, tss_dtor_t dtor) 522 { 523 #if defined(_TTHREAD_WIN32_) 524 /* FIXME: The destructor function is not supported yet... */ 525 if (dtor != NULL) 526 { 527 return thrd_error; 528 } 529 *key = TlsAlloc(); 530 if (*key == TLS_OUT_OF_INDEXES) 531 { 532 return thrd_error; 533 } 534 #else 535 if (pthread_key_create(key, dtor) != 0) 536 { 537 return thrd_error; 538 } 539 #endif 540 return thrd_success; 541 } 542 543 void tss_delete(tss_t key) 544 { 545 #if defined(_TTHREAD_WIN32_) 546 TlsFree(key); 547 #else 548 pthread_key_delete(key); 549 #endif 550 } 551 552 void *tss_get(tss_t key) 553 { 554 #if defined(_TTHREAD_WIN32_) 555 return TlsGetValue(key); 556 #else 557 return pthread_getspecific(key); 558 #endif 559 } 560 561 int tss_set(tss_t key, void *val) 562 { 563 #if defined(_TTHREAD_WIN32_) 564 if (TlsSetValue(key, val) == 0) 565 { 566 return thrd_error; 567 } 568 #else 569 if (pthread_setspecific(key, val) != 0) 570 { 571 return thrd_error; 572 } 573 #endif 574 return thrd_success; 575 } 576 577 #if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_) 578 int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts) 579 { 580 #if defined(_TTHREAD_WIN32_) 581 struct _timeb tb; 582 _ftime(&tb); 583 ts->tv_sec = (time_t)tb.time; 584 ts->tv_nsec = 1000000L * (long)tb.millitm; 585 #else 586 struct timeval tv; 587 gettimeofday(&tv, NULL); 588 ts->tv_sec = (time_t)tv.tv_sec; 589 ts->tv_nsec = 1000L * (long)tv.tv_usec; 590 #endif 591 return 0; 592 } 593 #endif // _TTHREAD_EMULATE_CLOCK_GETTIME_ 594