threads_win32.h revision 7ec681f3
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_TSS_DTOR_SLOTNUM 46 Max registerable TSS dtor number. 47*/ 48 49#if _WIN32_WINNT >= 0x0600 50// Prefer native WindowsAPI on newer environment. 51#if !defined(__MINGW32__) 52#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE 53#endif 54#endif 55#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE 56 57 58#include <windows.h> 59 60// check configuration 61#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600) 62#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600 63#endif 64 65/* Visual Studio 2015 and later */ 66#ifdef _MSC_VER 67#define HAVE_TIMESPEC_GET 68#endif 69 70/*---------------------------- macros ----------------------------*/ 71#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 72#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT 73#else 74#define ONCE_FLAG_INIT {0} 75#endif 76#define TSS_DTOR_ITERATIONS 1 77 78// FIXME: temporary non-standard hack to ease transition 79#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0} 80 81/*---------------------------- types ----------------------------*/ 82typedef CONDITION_VARIABLE cnd_t; 83 84typedef HANDLE thrd_t; 85 86typedef DWORD tss_t; 87 88typedef CRITICAL_SECTION mtx_t; 89 90#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 91typedef INIT_ONCE once_flag; 92#else 93typedef struct once_flag_t { 94 volatile LONG status; 95} once_flag; 96#endif 97 98 99static inline void * tss_get(tss_t key); 100static inline void thrd_yield(void); 101static inline int mtx_trylock(mtx_t *mtx); 102static inline int mtx_lock(mtx_t *mtx); 103static inline int mtx_unlock(mtx_t *mtx); 104 105/* 106Implementation limits: 107 - Conditionally emulation for "Initialization functions" 108 (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro) 109 - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop* 110*/ 111static void impl_tss_dtor_invoke(void); // forward decl. 112 113struct impl_thrd_param { 114 thrd_start_t func; 115 void *arg; 116}; 117 118static unsigned __stdcall impl_thrd_routine(void *p) 119{ 120 struct impl_thrd_param pack; 121 int code; 122 memcpy(&pack, p, sizeof(struct impl_thrd_param)); 123 free(p); 124 code = pack.func(pack.arg); 125 impl_tss_dtor_invoke(); 126 return (unsigned)code; 127} 128 129static time_t impl_timespec2msec(const struct timespec *ts) 130{ 131 return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L); 132} 133 134#ifdef HAVE_TIMESPEC_GET 135static DWORD impl_abs2relmsec(const struct timespec *abs_time) 136{ 137 const time_t abs_ms = impl_timespec2msec(abs_time); 138 struct timespec now; 139 timespec_get(&now, TIME_UTC); 140 const time_t now_ms = impl_timespec2msec(&now); 141 const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0; 142 return rel_ms; 143} 144#endif 145 146#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 147struct impl_call_once_param { void (*func)(void); }; 148static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) 149{ 150 struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter; 151 (param->func)(); 152 ((void)InitOnce); ((void)Context); // suppress warning 153 return TRUE; 154} 155#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 156 157static struct impl_tss_dtor_entry { 158 tss_t key; 159 tss_dtor_t dtor; 160} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM]; 161 162static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) 163{ 164 int i; 165 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { 166 if (!impl_tss_dtor_tbl[i].dtor) 167 break; 168 } 169 if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM) 170 return 1; 171 impl_tss_dtor_tbl[i].key = key; 172 impl_tss_dtor_tbl[i].dtor = dtor; 173 return 0; 174} 175 176static void impl_tss_dtor_invoke() 177{ 178 int i; 179 for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { 180 if (impl_tss_dtor_tbl[i].dtor) { 181 void* val = tss_get(impl_tss_dtor_tbl[i].key); 182 if (val) 183 (impl_tss_dtor_tbl[i].dtor)(val); 184 } 185 } 186} 187 188 189/*--------------- 7.25.2 Initialization functions ---------------*/ 190// 7.25.2.1 191static inline void 192call_once(once_flag *flag, void (*func)(void)) 193{ 194 assert(flag && func); 195#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE 196 { 197 struct impl_call_once_param param; 198 param.func = func; 199 InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL); 200 } 201#else 202 if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) { 203 (func)(); 204 InterlockedExchange(&flag->status, 2); 205 } else { 206 while (flag->status == 1) { 207 // busy loop! 208 thrd_yield(); 209 } 210 } 211#endif 212} 213 214 215/*------------- 7.25.3 Condition variable functions -------------*/ 216// 7.25.3.1 217static inline int 218cnd_broadcast(cnd_t *cond) 219{ 220 assert(cond != NULL); 221 WakeAllConditionVariable(cond); 222 return thrd_success; 223} 224 225// 7.25.3.2 226static inline void 227cnd_destroy(cnd_t *cond) 228{ 229 assert(cond != NULL); 230 // do nothing 231} 232 233// 7.25.3.3 234static inline int 235cnd_init(cnd_t *cond) 236{ 237 assert(cond != NULL); 238 InitializeConditionVariable(cond); 239 return thrd_success; 240} 241 242// 7.25.3.4 243static inline int 244cnd_signal(cnd_t *cond) 245{ 246 assert(cond != NULL); 247 WakeConditionVariable(cond); 248 return thrd_success; 249} 250 251// 7.25.3.5 252static inline int 253cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time) 254{ 255 assert(cond != NULL); 256 assert(mtx != NULL); 257 assert(abs_time != NULL); 258#ifdef HAVE_TIMESPEC_GET 259 const DWORD timeout = impl_abs2relmsec(abs_time); 260 if (SleepConditionVariableCS(cond, mtx, timeout)) 261 return thrd_success; 262 return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error; 263#else 264 return thrd_error; 265#endif 266} 267 268// 7.25.3.6 269static inline int 270cnd_wait(cnd_t *cond, mtx_t *mtx) 271{ 272 assert(cond != NULL); 273 assert(mtx != NULL); 274 SleepConditionVariableCS(cond, mtx, INFINITE); 275 return thrd_success; 276} 277 278 279/*-------------------- 7.25.4 Mutex functions --------------------*/ 280// 7.25.4.1 281static inline void 282mtx_destroy(mtx_t *mtx) 283{ 284 assert(mtx); 285 DeleteCriticalSection(mtx); 286} 287 288// 7.25.4.2 289static inline int 290mtx_init(mtx_t *mtx, int type) 291{ 292 assert(mtx != NULL); 293 if (type != mtx_plain && type != mtx_timed && type != mtx_try 294 && type != (mtx_plain|mtx_recursive) 295 && type != (mtx_timed|mtx_recursive) 296 && type != (mtx_try|mtx_recursive)) 297 return thrd_error; 298 InitializeCriticalSection(mtx); 299 return thrd_success; 300} 301 302// 7.25.4.3 303static inline int 304mtx_lock(mtx_t *mtx) 305{ 306 assert(mtx != NULL); 307 EnterCriticalSection(mtx); 308 return thrd_success; 309} 310 311// 7.25.4.4 312static inline int 313mtx_timedlock(mtx_t *mtx, const struct timespec *ts) 314{ 315 assert(mtx != NULL); 316 assert(ts != NULL); 317#ifdef HAVE_TIMESPEC_GET 318 while (mtx_trylock(mtx) != thrd_success) { 319 if (impl_abs2relmsec(ts) == 0) 320 return thrd_busy; 321 // busy loop! 322 thrd_yield(); 323 } 324 return thrd_success; 325#else 326 return thrd_error; 327#endif 328} 329 330// 7.25.4.5 331static inline int 332mtx_trylock(mtx_t *mtx) 333{ 334 assert(mtx != NULL); 335 return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy; 336} 337 338// 7.25.4.6 339static inline int 340mtx_unlock(mtx_t *mtx) 341{ 342 assert(mtx != NULL); 343 LeaveCriticalSection(mtx); 344 return thrd_success; 345} 346 347 348/*------------------- 7.25.5 Thread functions -------------------*/ 349// 7.25.5.1 350static inline int 351thrd_create(thrd_t *thr, thrd_start_t func, void *arg) 352{ 353 struct impl_thrd_param *pack; 354 uintptr_t handle; 355 assert(thr != NULL); 356 pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); 357 if (!pack) return thrd_nomem; 358 pack->func = func; 359 pack->arg = arg; 360 handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL); 361 if (handle == 0) { 362 if (errno == EAGAIN || errno == EACCES) 363 return thrd_nomem; 364 return thrd_error; 365 } 366 *thr = (thrd_t)handle; 367 return thrd_success; 368} 369 370#if 0 371// 7.25.5.2 372static inline thrd_t 373thrd_current(void) 374{ 375 HANDLE hCurrentThread; 376 BOOL bRet; 377 378 /* GetCurrentThread() returns a pseudo-handle, which we need 379 * to pass to DuplicateHandle(). Only the resulting handle can be used 380 * from other threads. 381 * 382 * Note that neither handle can be compared to the one by thread_create. 383 * Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId() 384 * can be compared directly. 385 * 386 * Other potential solutions would be: 387 * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations 388 * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread. 389 * 390 * Neither is particularly nice. 391 * 392 * Life would be much easier if C11 threads had different abstractions for 393 * threads and thread IDs, just like C++11 threads does... 394 */ 395 396 bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle 397 GetCurrentThread(), // source (pseudo) handle 398 GetCurrentProcess(), // target process 399 &hCurrentThread, // target handle 400 0, 401 FALSE, 402 DUPLICATE_SAME_ACCESS); 403 assert(bRet); 404 if (!bRet) { 405 hCurrentThread = GetCurrentThread(); 406 } 407 return hCurrentThread; 408} 409#endif 410 411// 7.25.5.3 412static inline int 413thrd_detach(thrd_t thr) 414{ 415 CloseHandle(thr); 416 return thrd_success; 417} 418 419// 7.25.5.4 420static inline int 421thrd_equal(thrd_t thr0, thrd_t thr1) 422{ 423 return GetThreadId(thr0) == GetThreadId(thr1); 424} 425 426// 7.25.5.5 427static inline void 428thrd_exit(int res) 429{ 430 impl_tss_dtor_invoke(); 431 _endthreadex((unsigned)res); 432} 433 434// 7.25.5.6 435static inline int 436thrd_join(thrd_t thr, int *res) 437{ 438 DWORD w, code; 439 w = WaitForSingleObject(thr, INFINITE); 440 if (w != WAIT_OBJECT_0) 441 return thrd_error; 442 if (res) { 443 if (!GetExitCodeThread(thr, &code)) { 444 CloseHandle(thr); 445 return thrd_error; 446 } 447 *res = (int)code; 448 } 449 CloseHandle(thr); 450 return thrd_success; 451} 452 453// 7.25.5.7 454static inline void 455thrd_sleep(const struct timespec *time_point, struct timespec *remaining) 456{ 457 assert(time_point); 458 assert(!remaining); /* not implemented */ 459 Sleep((DWORD)impl_timespec2msec(time_point)); 460} 461 462// 7.25.5.8 463static inline void 464thrd_yield(void) 465{ 466 SwitchToThread(); 467} 468 469 470/*----------- 7.25.6 Thread-specific storage functions -----------*/ 471// 7.25.6.1 472static inline int 473tss_create(tss_t *key, tss_dtor_t dtor) 474{ 475 assert(key != NULL); 476 *key = TlsAlloc(); 477 if (dtor) { 478 if (impl_tss_dtor_register(*key, dtor)) { 479 TlsFree(*key); 480 return thrd_error; 481 } 482 } 483 return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error; 484} 485 486// 7.25.6.2 487static inline void 488tss_delete(tss_t key) 489{ 490 TlsFree(key); 491} 492 493// 7.25.6.3 494static inline void * 495tss_get(tss_t key) 496{ 497 return TlsGetValue(key); 498} 499 500// 7.25.6.4 501static inline int 502tss_set(tss_t key, void *val) 503{ 504 return TlsSetValue(key, val) ? thrd_success : thrd_error; 505} 506 507 508/*-------------------- 7.25.7 Time functions --------------------*/ 509// 7.25.6.1 510#ifndef HAVE_TIMESPEC_GET 511static inline int 512timespec_get(struct timespec *ts, int base) 513{ 514 assert(ts != NULL); 515 if (base == TIME_UTC) { 516 ts->tv_sec = time(NULL); 517 ts->tv_nsec = 0; 518 return base; 519 } 520 return 0; 521} 522#endif 523