threads_win32.h revision eaf9affa5ec9c5fd919e4207ab80b4677650ac67
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/* 38Configuration 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 ----------------------------*/ 91typedef 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 104typedef HANDLE thrd_t; 105 106typedef DWORD tss_t; 107 108typedef CRITICAL_SECTION mtx_t; 109 110#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 111typedef INIT_ONCE once_flag; 112#else 113typedef struct once_flag_t { 114 volatile LONG status; 115} once_flag; 116#endif 117 118 119static inline void * tss_get(tss_t key); 120static inline void thrd_yield(void); 121static inline int mtx_trylock(mtx_t *mtx); 122static inline int mtx_lock(mtx_t *mtx); 123static inline int mtx_unlock(mtx_t *mtx); 124 125/* 126Implementation 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*/ 131static void impl_tss_dtor_invoke(void); // forward decl. 132 133struct impl_thrd_param { 134 thrd_start_t func; 135 void *arg; 136}; 137 138static 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 149static 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 155struct impl_call_once_param { void (*func)(void); }; 156static 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/* 167Note: 168 The implementation of condition variable is ported from Boost.Interprocess 169 See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp 170*/ 171static 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 209static 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 262static 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 267static 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 281static 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 296static inline void 297call_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 322static inline int 323cnd_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 335static inline void 336cnd_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 349static inline int 350cnd_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 367static inline int 368cnd_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 380static inline int 381cnd_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 394static inline int 395cnd_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 409static inline void 410mtx_destroy(mtx_t *mtx) 411{ 412 assert(mtx); 413 DeleteCriticalSection(mtx); 414} 415 416// 7.25.4.2 417static inline int 418mtx_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 431static inline int 432mtx_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 440static inline int 441mtx_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 458static inline int 459mtx_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 466static inline int 467mtx_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 477static inline int 478thrd_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 499static inline thrd_t 500thrd_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 535static inline int 536thrd_detach(thrd_t thr) 537{ 538 CloseHandle(thr); 539 return thrd_success; 540} 541 542// 7.25.5.4 543static inline int 544thrd_equal(thrd_t thr0, thrd_t thr1) 545{ 546 return GetThreadId(thr0) == GetThreadId(thr1); 547} 548 549// 7.25.5.5 550static inline void 551thrd_exit(int res) 552{ 553 impl_tss_dtor_invoke(); 554 _endthreadex((unsigned)res); 555} 556 557// 7.25.5.6 558static inline int 559thrd_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 577static inline void 578thrd_sleep(const xtime *xt) 579{ 580 assert(xt); 581 Sleep(impl_xtime2msec(xt)); 582} 583 584// 7.25.5.8 585static inline void 586thrd_yield(void) 587{ 588 SwitchToThread(); 589} 590 591 592/*----------- 7.25.6 Thread-specific storage functions -----------*/ 593// 7.25.6.1 594static inline int 595tss_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 609static inline void 610tss_delete(tss_t key) 611{ 612 TlsFree(key); 613} 614 615// 7.25.6.3 616static inline void * 617tss_get(tss_t key) 618{ 619 return TlsGetValue(key); 620} 621 622// 7.25.6.4 623static inline int 624tss_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 632static inline int 633xtime_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