Path: blob/21.2-virgl/include/c11/threads_win32.h
4550 views
/*1* C11 <threads.h> emulation library2*3* (C) Copyright yohhoy 2012.4* Distributed under the Boost Software License, Version 1.0.5*6* Permission is hereby granted, free of charge, to any person or organization7* obtaining a copy of the software and accompanying documentation covered by8* this license (the "Software") to use, reproduce, display, distribute,9* execute, and transmit the Software, and to prepare [[derivative work]]s of the10* Software, and to permit third-parties to whom the Software is furnished to11* do so, all subject to the following:12*13* The copyright notices in the Software and this entire statement, including14* the above license grant, this restriction and the following disclaimer,15* must be included in all copies of the Software, in whole or in part, and16* all derivative works of the Software, unless such copies or derivative17* works are solely in the form of machine-executable object code generated by18* a source language processor.19*20* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR21* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,22* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT23* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE24* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,25* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER26* DEALINGS IN THE SOFTWARE.27*/28#ifndef assert29#include <assert.h>30#endif31#include <limits.h>32#include <errno.h>33#include <process.h> // MSVCRT34#include <stdlib.h>3536/*37Configuration macro:3839EMULATED_THREADS_USE_NATIVE_CALL_ONCE40Use native WindowsAPI one-time initialization function.41(requires WinVista or later)42Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.4344EMULATED_THREADS_TSS_DTOR_SLOTNUM45Max registerable TSS dtor number.46*/4748#if _WIN32_WINNT >= 0x060049// Prefer native WindowsAPI on newer environment.50#if !defined(__MINGW32__)51#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE52#endif53#endif54#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE555657#include <windows.h>5859// check configuration60#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)61#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x060062#endif6364/* Visual Studio 2015 and later */65#ifdef _MSC_VER66#define HAVE_TIMESPEC_GET67#endif6869/*---------------------------- macros ----------------------------*/70#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE71#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT72#else73#define ONCE_FLAG_INIT {0}74#endif75#define TSS_DTOR_ITERATIONS 17677// FIXME: temporary non-standard hack to ease transition78#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}7980/*---------------------------- types ----------------------------*/81typedef CONDITION_VARIABLE cnd_t;8283typedef HANDLE thrd_t;8485typedef DWORD tss_t;8687typedef CRITICAL_SECTION mtx_t;8889#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE90typedef INIT_ONCE once_flag;91#else92typedef struct once_flag_t {93volatile LONG status;94} once_flag;95#endif969798static inline void * tss_get(tss_t key);99static inline void thrd_yield(void);100static inline int mtx_trylock(mtx_t *mtx);101static inline int mtx_lock(mtx_t *mtx);102static inline int mtx_unlock(mtx_t *mtx);103104/*105Implementation limits:106- Conditionally emulation for "Initialization functions"107(see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)108- Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*109*/110static void impl_tss_dtor_invoke(void); // forward decl.111112struct impl_thrd_param {113thrd_start_t func;114void *arg;115};116117static unsigned __stdcall impl_thrd_routine(void *p)118{119struct impl_thrd_param pack;120int code;121memcpy(&pack, p, sizeof(struct impl_thrd_param));122free(p);123code = pack.func(pack.arg);124impl_tss_dtor_invoke();125return (unsigned)code;126}127128static time_t impl_timespec2msec(const struct timespec *ts)129{130return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);131}132133#ifdef HAVE_TIMESPEC_GET134static DWORD impl_abs2relmsec(const struct timespec *abs_time)135{136const time_t abs_ms = impl_timespec2msec(abs_time);137struct timespec now;138timespec_get(&now, TIME_UTC);139const time_t now_ms = impl_timespec2msec(&now);140const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0;141return rel_ms;142}143#endif144145#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE146struct impl_call_once_param { void (*func)(void); };147static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)148{149struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;150(param->func)();151((void)InitOnce); ((void)Context); // suppress warning152return TRUE;153}154#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE155156static struct impl_tss_dtor_entry {157tss_t key;158tss_dtor_t dtor;159} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];160161static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)162{163int i;164for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {165if (!impl_tss_dtor_tbl[i].dtor)166break;167}168if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)169return 1;170impl_tss_dtor_tbl[i].key = key;171impl_tss_dtor_tbl[i].dtor = dtor;172return 0;173}174175static void impl_tss_dtor_invoke()176{177int i;178for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {179if (impl_tss_dtor_tbl[i].dtor) {180void* val = tss_get(impl_tss_dtor_tbl[i].key);181if (val)182(impl_tss_dtor_tbl[i].dtor)(val);183}184}185}186187188/*--------------- 7.25.2 Initialization functions ---------------*/189// 7.25.2.1190static inline void191call_once(once_flag *flag, void (*func)(void))192{193assert(flag && func);194#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE195{196struct impl_call_once_param param;197param.func = func;198InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL);199}200#else201if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {202(func)();203InterlockedExchange(&flag->status, 2);204} else {205while (flag->status == 1) {206// busy loop!207thrd_yield();208}209}210#endif211}212213214/*------------- 7.25.3 Condition variable functions -------------*/215// 7.25.3.1216static inline int217cnd_broadcast(cnd_t *cond)218{219assert(cond != NULL);220WakeAllConditionVariable(cond);221return thrd_success;222}223224// 7.25.3.2225static inline void226cnd_destroy(cnd_t *cond)227{228assert(cond != NULL);229// do nothing230}231232// 7.25.3.3233static inline int234cnd_init(cnd_t *cond)235{236assert(cond != NULL);237InitializeConditionVariable(cond);238return thrd_success;239}240241// 7.25.3.4242static inline int243cnd_signal(cnd_t *cond)244{245assert(cond != NULL);246WakeConditionVariable(cond);247return thrd_success;248}249250// 7.25.3.5251static inline int252cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)253{254assert(cond != NULL);255assert(mtx != NULL);256assert(abs_time != NULL);257#ifdef HAVE_TIMESPEC_GET258const DWORD timeout = impl_abs2relmsec(abs_time);259if (SleepConditionVariableCS(cond, mtx, timeout))260return thrd_success;261return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;262#else263return thrd_error;264#endif265}266267// 7.25.3.6268static inline int269cnd_wait(cnd_t *cond, mtx_t *mtx)270{271assert(cond != NULL);272assert(mtx != NULL);273SleepConditionVariableCS(cond, mtx, INFINITE);274return thrd_success;275}276277278/*-------------------- 7.25.4 Mutex functions --------------------*/279// 7.25.4.1280static inline void281mtx_destroy(mtx_t *mtx)282{283assert(mtx);284DeleteCriticalSection(mtx);285}286287// 7.25.4.2288static inline int289mtx_init(mtx_t *mtx, int type)290{291assert(mtx != NULL);292if (type != mtx_plain && type != mtx_timed && type != mtx_try293&& type != (mtx_plain|mtx_recursive)294&& type != (mtx_timed|mtx_recursive)295&& type != (mtx_try|mtx_recursive))296return thrd_error;297InitializeCriticalSection(mtx);298return thrd_success;299}300301// 7.25.4.3302static inline int303mtx_lock(mtx_t *mtx)304{305assert(mtx != NULL);306EnterCriticalSection(mtx);307return thrd_success;308}309310// 7.25.4.4311static inline int312mtx_timedlock(mtx_t *mtx, const struct timespec *ts)313{314assert(mtx != NULL);315assert(ts != NULL);316#ifdef HAVE_TIMESPEC_GET317while (mtx_trylock(mtx) != thrd_success) {318if (impl_abs2relmsec(ts) == 0)319return thrd_busy;320// busy loop!321thrd_yield();322}323return thrd_success;324#else325return thrd_error;326#endif327}328329// 7.25.4.5330static inline int331mtx_trylock(mtx_t *mtx)332{333assert(mtx != NULL);334return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;335}336337// 7.25.4.6338static inline int339mtx_unlock(mtx_t *mtx)340{341assert(mtx != NULL);342LeaveCriticalSection(mtx);343return thrd_success;344}345346347/*------------------- 7.25.5 Thread functions -------------------*/348// 7.25.5.1349static inline int350thrd_create(thrd_t *thr, thrd_start_t func, void *arg)351{352struct impl_thrd_param *pack;353uintptr_t handle;354assert(thr != NULL);355pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));356if (!pack) return thrd_nomem;357pack->func = func;358pack->arg = arg;359handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);360if (handle == 0) {361if (errno == EAGAIN || errno == EACCES)362return thrd_nomem;363return thrd_error;364}365*thr = (thrd_t)handle;366return thrd_success;367}368369#if 0370// 7.25.5.2371static inline thrd_t372thrd_current(void)373{374HANDLE hCurrentThread;375BOOL bRet;376377/* GetCurrentThread() returns a pseudo-handle, which we need378* to pass to DuplicateHandle(). Only the resulting handle can be used379* from other threads.380*381* Note that neither handle can be compared to the one by thread_create.382* Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()383* can be compared directly.384*385* Other potential solutions would be:386* - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations387* - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.388*389* Neither is particularly nice.390*391* Life would be much easier if C11 threads had different abstractions for392* threads and thread IDs, just like C++11 threads does...393*/394395bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle396GetCurrentThread(), // source (pseudo) handle397GetCurrentProcess(), // target process398&hCurrentThread, // target handle3990,400FALSE,401DUPLICATE_SAME_ACCESS);402assert(bRet);403if (!bRet) {404hCurrentThread = GetCurrentThread();405}406return hCurrentThread;407}408#endif409410// 7.25.5.3411static inline int412thrd_detach(thrd_t thr)413{414CloseHandle(thr);415return thrd_success;416}417418// 7.25.5.4419static inline int420thrd_equal(thrd_t thr0, thrd_t thr1)421{422return GetThreadId(thr0) == GetThreadId(thr1);423}424425// 7.25.5.5426static inline void427thrd_exit(int res)428{429impl_tss_dtor_invoke();430_endthreadex((unsigned)res);431}432433// 7.25.5.6434static inline int435thrd_join(thrd_t thr, int *res)436{437DWORD w, code;438w = WaitForSingleObject(thr, INFINITE);439if (w != WAIT_OBJECT_0)440return thrd_error;441if (res) {442if (!GetExitCodeThread(thr, &code)) {443CloseHandle(thr);444return thrd_error;445}446*res = (int)code;447}448CloseHandle(thr);449return thrd_success;450}451452// 7.25.5.7453static inline void454thrd_sleep(const struct timespec *time_point, struct timespec *remaining)455{456assert(time_point);457assert(!remaining); /* not implemented */458Sleep((DWORD)impl_timespec2msec(time_point));459}460461// 7.25.5.8462static inline void463thrd_yield(void)464{465SwitchToThread();466}467468469/*----------- 7.25.6 Thread-specific storage functions -----------*/470// 7.25.6.1471static inline int472tss_create(tss_t *key, tss_dtor_t dtor)473{474assert(key != NULL);475*key = TlsAlloc();476if (dtor) {477if (impl_tss_dtor_register(*key, dtor)) {478TlsFree(*key);479return thrd_error;480}481}482return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;483}484485// 7.25.6.2486static inline void487tss_delete(tss_t key)488{489TlsFree(key);490}491492// 7.25.6.3493static inline void *494tss_get(tss_t key)495{496return TlsGetValue(key);497}498499// 7.25.6.4500static inline int501tss_set(tss_t key, void *val)502{503return TlsSetValue(key, val) ? thrd_success : thrd_error;504}505506507/*-------------------- 7.25.7 Time functions --------------------*/508// 7.25.6.1509#ifndef HAVE_TIMESPEC_GET510static inline int511timespec_get(struct timespec *ts, int base)512{513assert(ts != NULL);514if (base == TIME_UTC) {515ts->tv_sec = time(NULL);516ts->tv_nsec = 0;517return base;518}519return 0;520}521#endif522523524