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