1 /* 2 * C11 <threads.h> emulation library 3 * 4 * (C) Copyright yohhoy 2012. 5 * Distributed under the Boost Software License, Version 1.0. 6 * 7 * Permission is hereby granted, free of charge, to any person or organization 8 * obtaining a copy of the software and accompanying documentation covered by 9 * this license (the "Software") to use, reproduce, display, distribute, 10 * execute, and transmit the Software, and to prepare [[derivative work]]s of the 11 * Software, and to permit third-parties to whom the Software is furnished to 12 * do so, all subject to the following: 13 * 14 * The copyright notices in the Software and this entire statement, including 15 * the above license grant, this restriction and the following disclaimer, 16 * must be included in all copies of the Software, in whole or in part, and 17 * all derivative works of the Software, unless such copies or derivative 18 * works are solely in the form of machine-executable object code generated by 19 * a source language processor. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 * DEALINGS IN THE SOFTWARE. 28 */ 29 #ifndef assert 30 #include <assert.h> 31 #endif 32 #include <limits.h> 33 #include <errno.h> 34 #include <process.h> // MSVCRT 35 #include <stdlib.h> 36 37 /* 38 Configuration macro: 39 40 EMULATED_THREADS_USE_NATIVE_CALL_ONCE 41 Use native WindowsAPI one-time initialization function. 42 (requires WinVista or later) 43 Otherwise emulate by mtx_trylock() + *busy loop* for WinXP. 44 45 EMULATED_THREADS_USE_NATIVE_CV 46 Use native WindowsAPI condition variable object. 47 (requires WinVista or later) 48 Otherwise use emulated implementation for WinXP. 49 50 EMULATED_THREADS_TSS_DTOR_SLOTNUM 51 Max registerable TSS dtor number. 52 */ 53 54 // XXX: Retain XP compatability 55 #if 0 56 #if _WIN32_WINNT >= 0x0600 57 // Prefer native WindowsAPI on newer environment. 58 #if !defined(__MINGW32__) 59 #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE 60 #endif 61 #define EMULATED_THREADS_USE_NATIVE_CV 62 #endif 63 #endif 64 #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE 65 66 67 #include <windows.h> 68 69 // check configuration 70 #if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600) 71 #error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600 72 #endif 73 74 #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600) 75 #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600 76 #endif 77 78 79 /*---------------------------- macros ----------------------------*/ 80 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 81 #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT 82 #else 83 #define ONCE_FLAG_INIT {0} 84 #endif 85 #define TSS_DTOR_ITERATIONS 1 86 87 // FIXME: temporary non-standard hack to ease transition 88 #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0} 89 90 /*---------------------------- types ----------------------------*/ 91 typedef struct cnd_t { 92 #ifdef EMULATED_THREADS_USE_NATIVE_CV 93 CONDITION_VARIABLE condvar; 94 #else 95 int blocked; 96 int gone; 97 int to_unblock; 98 HANDLE sem_queue; 99 HANDLE sem_gate; 100 CRITICAL_SECTION monitor; 101 #endif 102 } cnd_t; 103 104 typedef HANDLE thrd_t; 105 106 typedef DWORD tss_t; 107 108 typedef CRITICAL_SECTION mtx_t; 109 110 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 111 typedef INIT_ONCE once_flag; 112 #else 113 typedef struct once_flag_t { 114 volatile LONG status; 115 } once_flag; 116 #endif 117 118 119 static inline void * tss_get(tss_t key); 120 static inline void thrd_yield(void); 121 static inline int mtx_trylock(mtx_t *mtx); 122 static inline int mtx_lock(mtx_t *mtx); 123 static inline int mtx_unlock(mtx_t *mtx); 124 125 /* 126 Implementation limits: 127 - Conditionally emulation for "Initialization functions" 128 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro) 129 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop* 130 */ 131 static void impl_tss_dtor_invoke(void); // forward decl. 132 133 struct impl_thrd_param { 134 thrd_start_t func; 135 void *arg; 136 }; 137 138 static unsigned __stdcall impl_thrd_routine(void *p) 139 { 140 struct impl_thrd_param pack; 141 int code; 142 memcpy(&pack, p, sizeof(struct impl_thrd_param)); 143 free(p); 144 code = pack.func(pack.arg); 145 impl_tss_dtor_invoke(); 146 return (unsigned)code; 147 } 148 149 static DWORD impl_xtime2msec(const xtime *xt) 150 { 151 return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L)); 152 } 153 154 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 155 struct impl_call_once_param { void (*func)(void); }; 156 static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) 157 { 158 struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter; 159 (param->func)(); 160 ((void)InitOnce); ((void)Context); // suppress warning 161 return TRUE; 162 } 163 #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 164 165 #ifndef EMULATED_THREADS_USE_NATIVE_CV 166 /* 167 Note: 168 The implementation of condition variable is ported from Boost.Interprocess 169 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp 170 */ 171 static void impl_cond_do_signal(cnd_t *cond, int broadcast) 172 { 173 int nsignal = 0; 174 175 EnterCriticalSection(&cond->monitor); 176 if (cond->to_unblock != 0) { 177 if (cond->blocked == 0) { 178 LeaveCriticalSection(&cond->monitor); 179 return; 180 } 181 if (broadcast) { 182 cond->to_unblock += nsignal = cond->blocked; 183 cond->blocked = 0; 184 } else { 185 nsignal = 1; 186 cond->to_unblock++; 187 cond->blocked--; 188 } 189 } else if (cond->blocked > cond->gone) { 190 WaitForSingleObject(cond->sem_gate, INFINITE); 191 if (cond->gone != 0) { 192 cond->blocked -= cond->gone; 193 cond->gone = 0; 194 } 195 if (broadcast) { 196 nsignal = cond->to_unblock = cond->blocked; 197 cond->blocked = 0; 198 } else { 199 nsignal = cond->to_unblock = 1; 200 cond->blocked--; 201 } 202 } 203 LeaveCriticalSection(&cond->monitor); 204 205 if (0 < nsignal) 206 ReleaseSemaphore(cond->sem_queue, nsignal, NULL); 207 } 208 209 static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt) 210 { 211 int nleft = 0; 212 int ngone = 0; 213 int timeout = 0; 214 DWORD w; 215 216 WaitForSingleObject(cond->sem_gate, INFINITE); 217 cond->blocked++; 218 ReleaseSemaphore(cond->sem_gate, 1, NULL); 219 220 mtx_unlock(mtx); 221 222 w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE); 223 timeout = (w == WAIT_TIMEOUT); 224 225 EnterCriticalSection(&cond->monitor); 226 if ((nleft = cond->to_unblock) != 0) { 227 if (timeout) { 228 if (cond->blocked != 0) { 229 cond->blocked--; 230 } else { 231 cond->gone++; 232 } 233 } 234 if (--cond->to_unblock == 0) { 235 if (cond->blocked != 0) { 236 ReleaseSemaphore(cond->sem_gate, 1, NULL); 237 nleft = 0; 238 } 239 else if ((ngone = cond->gone) != 0) { 240 cond->gone = 0; 241 } 242 } 243 } else if (++cond->gone == INT_MAX/2) { 244 WaitForSingleObject(cond->sem_gate, INFINITE); 245 cond->blocked -= cond->gone; 246 ReleaseSemaphore(cond->sem_gate, 1, NULL); 247 cond->gone = 0; 248 } 249 LeaveCriticalSection(&cond->monitor); 250 251 if (nleft == 1) { 252 while (ngone--) 253 WaitForSingleObject(cond->sem_queue, INFINITE); 254 ReleaseSemaphore(cond->sem_gate, 1, NULL); 255 } 256 257 mtx_lock(mtx); 258 return timeout ? thrd_busy : thrd_success; 259 } 260 #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV 261 262 static struct impl_tss_dtor_entry { 263 tss_t key; 264 tss_dtor_t dtor; 265 } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM]; 266 267 static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) 268 { 269 int i; 270 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { 271 if (!impl_tss_dtor_tbl[i].dtor) 272 break; 273 } 274 if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM) 275 return 1; 276 impl_tss_dtor_tbl[i].key = key; 277 impl_tss_dtor_tbl[i].dtor = dtor; 278 return 0; 279 } 280 281 static void impl_tss_dtor_invoke() 282 { 283 int i; 284 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { 285 if (impl_tss_dtor_tbl[i].dtor) { 286 void* val = tss_get(impl_tss_dtor_tbl[i].key); 287 if (val) 288 (impl_tss_dtor_tbl[i].dtor)(val); 289 } 290 } 291 } 292 293 294 /*--------------- 7.25.2 Initialization functions ---------------*/ 295 // 7.25.2.1 296 static inline void 297 call_once(once_flag *flag, void (*func)(void)) 298 { 299 assert(flag && func); 300 #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 301 { 302 struct impl_call_once_param param; 303 param.func = func; 304 InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL); 305 } 306 #else 307 if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) { 308 (func)(); 309 InterlockedExchange(&flag->status, 2); 310 } else { 311 while (flag->status == 1) { 312 // busy loop! 313 thrd_yield(); 314 } 315 } 316 #endif 317 } 318 319 320 /*------------- 7.25.3 Condition variable functions -------------*/ 321 // 7.25.3.1 322 static inline int 323 cnd_broadcast(cnd_t *cond) 324 { 325 if (!cond) return thrd_error; 326 #ifdef EMULATED_THREADS_USE_NATIVE_CV 327 WakeAllConditionVariable(&cond->condvar); 328 #else 329 impl_cond_do_signal(cond, 1); 330 #endif 331 return thrd_success; 332 } 333 334 // 7.25.3.2 335 static inline void 336 cnd_destroy(cnd_t *cond) 337 { 338 assert(cond); 339 #ifdef EMULATED_THREADS_USE_NATIVE_CV 340 // do nothing 341 #else 342 CloseHandle(cond->sem_queue); 343 CloseHandle(cond->sem_gate); 344 DeleteCriticalSection(&cond->monitor); 345 #endif 346 } 347 348 // 7.25.3.3 349 static inline int 350 cnd_init(cnd_t *cond) 351 { 352 if (!cond) return thrd_error; 353 #ifdef EMULATED_THREADS_USE_NATIVE_CV 354 InitializeConditionVariable(&cond->condvar); 355 #else 356 cond->blocked = 0; 357 cond->gone = 0; 358 cond->to_unblock = 0; 359 cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL); 360 cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL); 361 InitializeCriticalSection(&cond->monitor); 362 #endif 363 return thrd_success; 364 } 365 366 // 7.25.3.4 367 static inline int 368 cnd_signal(cnd_t *cond) 369 { 370 if (!cond) return thrd_error; 371 #ifdef EMULATED_THREADS_USE_NATIVE_CV 372 WakeConditionVariable(&cond->condvar); 373 #else 374 impl_cond_do_signal(cond, 0); 375 #endif 376 return thrd_success; 377 } 378 379 // 7.25.3.5 380 static inline int 381 cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) 382 { 383 if (!cond || !mtx || !xt) return thrd_error; 384 #ifdef EMULATED_THREADS_USE_NATIVE_CV 385 if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt))) 386 return thrd_success; 387 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error; 388 #else 389 return impl_cond_do_wait(cond, mtx, xt); 390 #endif 391 } 392 393 // 7.25.3.6 394 static inline int 395 cnd_wait(cnd_t *cond, mtx_t *mtx) 396 { 397 if (!cond || !mtx) return thrd_error; 398 #ifdef EMULATED_THREADS_USE_NATIVE_CV 399 SleepConditionVariableCS(&cond->condvar, mtx, INFINITE); 400 #else 401 impl_cond_do_wait(cond, mtx, NULL); 402 #endif 403 return thrd_success; 404 } 405 406 407 /*-------------------- 7.25.4 Mutex functions --------------------*/ 408 // 7.25.4.1 409 static inline void 410 mtx_destroy(mtx_t *mtx) 411 { 412 assert(mtx); 413 DeleteCriticalSection(mtx); 414 } 415 416 // 7.25.4.2 417 static inline int 418 mtx_init(mtx_t *mtx, int type) 419 { 420 if (!mtx) return thrd_error; 421 if (type != mtx_plain && type != mtx_timed && type != mtx_try 422 && type != (mtx_plain|mtx_recursive) 423 && type != (mtx_timed|mtx_recursive) 424 && type != (mtx_try|mtx_recursive)) 425 return thrd_error; 426 InitializeCriticalSection(mtx); 427 return thrd_success; 428 } 429 430 // 7.25.4.3 431 static inline int 432 mtx_lock(mtx_t *mtx) 433 { 434 if (!mtx) return thrd_error; 435 EnterCriticalSection(mtx); 436 return thrd_success; 437 } 438 439 // 7.25.4.4 440 static inline int 441 mtx_timedlock(mtx_t *mtx, const xtime *xt) 442 { 443 time_t expire, now; 444 if (!mtx || !xt) return thrd_error; 445 expire = time(NULL); 446 expire += xt->sec; 447 while (mtx_trylock(mtx) != thrd_success) { 448 now = time(NULL); 449 if (expire < now) 450 return thrd_busy; 451 // busy loop! 452 thrd_yield(); 453 } 454 return thrd_success; 455 } 456 457 // 7.25.4.5 458 static inline int 459 mtx_trylock(mtx_t *mtx) 460 { 461 if (!mtx) return thrd_error; 462 return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy; 463 } 464 465 // 7.25.4.6 466 static inline int 467 mtx_unlock(mtx_t *mtx) 468 { 469 if (!mtx) return thrd_error; 470 LeaveCriticalSection(mtx); 471 return thrd_success; 472 } 473 474 475 /*------------------- 7.25.5 Thread functions -------------------*/ 476 // 7.25.5.1 477 static inline int 478 thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 479 { 480 struct impl_thrd_param *pack; 481 uintptr_t handle; 482 if (!thr) return thrd_error; 483 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); 484 if (!pack) return thrd_nomem; 485 pack->func = func; 486 pack->arg = arg; 487 handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL); 488 if (handle == 0) { 489 if (errno == EAGAIN || errno == EACCES) 490 return thrd_nomem; 491 return thrd_error; 492 } 493 *thr = (thrd_t)handle; 494 return thrd_success; 495 } 496 497 #if 0 498 // 7.25.5.2 499 static inline thrd_t 500 thrd_current(void) 501 { 502 HANDLE hCurrentThread; 503 BOOL bRet; 504 505 /* GetCurrentThread() returns a pseudo-handle, which is useless. We need 506 * to call DuplicateHandle to get a real handle. However the handle value 507 * will not match the one returned by thread_create. 508 * 509 * Other potential solutions would be: 510 * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations 511 * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread. 512 * 513 * Neither is particularly nice. 514 * 515 * Life would be much easier if C11 threads had different abstractions for 516 * threads and thread IDs, just like C++11 threads does... 517 */ 518 519 bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle 520 GetCurrentThread(), // source (pseudo) handle 521 GetCurrentProcess(), // target process 522 &hCurrentThread, // target handle 523 0, 524 FALSE, 525 DUPLICATE_SAME_ACCESS); 526 assert(bRet); 527 if (!bRet) { 528 hCurrentThread = GetCurrentThread(); 529 } 530 return hCurrentThread; 531 } 532 #endif 533 534 // 7.25.5.3 535 static inline int 536 thrd_detach(thrd_t thr) 537 { 538 CloseHandle(thr); 539 return thrd_success; 540 } 541 542 // 7.25.5.4 543 static inline int 544 thrd_equal(thrd_t thr0, thrd_t thr1) 545 { 546 return GetThreadId(thr0) == GetThreadId(thr1); 547 } 548 549 // 7.25.5.5 550 static inline void 551 thrd_exit(int res) 552 { 553 impl_tss_dtor_invoke(); 554 _endthreadex((unsigned)res); 555 } 556 557 // 7.25.5.6 558 static inline int 559 thrd_join(thrd_t thr, int *res) 560 { 561 DWORD w, code; 562 w = WaitForSingleObject(thr, INFINITE); 563 if (w != WAIT_OBJECT_0) 564 return thrd_error; 565 if (res) { 566 if (!GetExitCodeThread(thr, &code)) { 567 CloseHandle(thr); 568 return thrd_error; 569 } 570 *res = (int)code; 571 } 572 CloseHandle(thr); 573 return thrd_success; 574 } 575 576 // 7.25.5.7 577 static inline void 578 thrd_sleep(const xtime *xt) 579 { 580 assert(xt); 581 Sleep(impl_xtime2msec(xt)); 582 } 583 584 // 7.25.5.8 585 static inline void 586 thrd_yield(void) 587 { 588 SwitchToThread(); 589 } 590 591 592 /*----------- 7.25.6 Thread-specific storage functions -----------*/ 593 // 7.25.6.1 594 static inline int 595 tss_create(tss_t *key, tss_dtor_t dtor) 596 { 597 if (!key) return thrd_error; 598 *key = TlsAlloc(); 599 if (dtor) { 600 if (impl_tss_dtor_register(*key, dtor)) { 601 TlsFree(*key); 602 return thrd_error; 603 } 604 } 605 return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error; 606 } 607 608 // 7.25.6.2 609 static inline void 610 tss_delete(tss_t key) 611 { 612 TlsFree(key); 613 } 614 615 // 7.25.6.3 616 static inline void * 617 tss_get(tss_t key) 618 { 619 return TlsGetValue(key); 620 } 621 622 // 7.25.6.4 623 static inline int 624 tss_set(tss_t key, void *val) 625 { 626 return TlsSetValue(key, val) ? thrd_success : thrd_error; 627 } 628 629 630 /*-------------------- 7.25.7 Time functions --------------------*/ 631 // 7.25.6.1 632 static inline int 633 xtime_get(xtime *xt, int base) 634 { 635 if (!xt) return 0; 636 if (base == TIME_UTC) { 637 xt->sec = time(NULL); 638 xt->nsec = 0; 639 return base; 640 } 641 return 0; 642 } 643