| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /*
- * C11 <threads.h> emulation library
- *
- * (C) Copyright yohhoy 2012.
- * Distributed under the Boost Software License, Version 1.0.
- *
- * Permission is hereby granted, free of charge, to any person or organization
- * obtaining a copy of the software and accompanying documentation covered by
- * this license (the "Software") to use, reproduce, display, distribute,
- * execute, and transmit the Software, and to prepare [[derivative work]]s of the
- * Software, and to permit third-parties to whom the Software is furnished to
- * do so, all subject to the following:
- *
- * The copyright notices in the Software and this entire statement, including
- * the above license grant, this restriction and the following disclaimer,
- * must be included in all copies of the Software, in whole or in part, and
- * all derivative works of the Software, unless such copies or derivative
- * works are solely in the form of machine-executable object code generated by
- * a source language processor.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- #ifndef assert
- #include <assert.h>
- #endif
- #include <limits.h>
- #include <errno.h>
- #include <process.h> // MSVCRT
- #include <stdlib.h>
-
- /*
- Configuration macro:
-
- EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- Use native WindowsAPI one-time initialization function.
- (requires WinVista or later)
- Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
-
- EMULATED_THREADS_USE_NATIVE_CV
- Use native WindowsAPI condition variable object.
- (requires WinVista or later)
- Otherwise use emulated implementation for WinXP.
-
- EMULATED_THREADS_TSS_DTOR_SLOTNUM
- Max registerable TSS dtor number.
- */
-
- // XXX: Retain XP compatability
- #if 0
- #if _WIN32_WINNT >= 0x0600
- // Prefer native WindowsAPI on newer environment.
- #if !defined(__MINGW32__)
- #define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- #endif
- #define EMULATED_THREADS_USE_NATIVE_CV
- #endif
- #endif
- #define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
-
-
- #include <windows.h>
-
- // check configuration
- #if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600)
- #error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
- #endif
-
- #if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600)
- #error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600
- #endif
-
-
- /*---------------------------- macros ----------------------------*/
- #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- #define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT
- #else
- #define ONCE_FLAG_INIT {0}
- #endif
- #define TSS_DTOR_ITERATIONS 1
-
- // FIXME: temporary non-standard hack to ease transition
- #define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}
-
- /*---------------------------- types ----------------------------*/
- typedef struct cnd_t {
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- CONDITION_VARIABLE condvar;
- #else
- int blocked;
- int gone;
- int to_unblock;
- HANDLE sem_queue;
- HANDLE sem_gate;
- CRITICAL_SECTION monitor;
- #endif
- } cnd_t;
-
- typedef HANDLE thrd_t;
-
- typedef DWORD tss_t;
-
- typedef CRITICAL_SECTION mtx_t;
-
- #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- typedef INIT_ONCE once_flag;
- #else
- typedef struct once_flag_t {
- volatile LONG status;
- } once_flag;
- #endif
-
-
- static inline void * tss_get(tss_t key);
- static inline void thrd_yield(void);
- static inline int mtx_trylock(mtx_t *mtx);
- static inline int mtx_lock(mtx_t *mtx);
- static inline int mtx_unlock(mtx_t *mtx);
-
- /*
- Implementation limits:
- - Conditionally emulation for "Initialization functions"
- (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
- - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
- */
- static void impl_tss_dtor_invoke(void); // forward decl.
-
- struct impl_thrd_param {
- thrd_start_t func;
- void *arg;
- };
-
- static unsigned __stdcall impl_thrd_routine(void *p)
- {
- struct impl_thrd_param pack;
- int code;
- memcpy(&pack, p, sizeof(struct impl_thrd_param));
- free(p);
- code = pack.func(pack.arg);
- impl_tss_dtor_invoke();
- return (unsigned)code;
- }
-
- static DWORD impl_xtime2msec(const xtime *xt)
- {
- return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L));
- }
-
- #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- struct impl_call_once_param { void (*func)(void); };
- static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
- {
- struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter;
- (param->func)();
- ((void)InitOnce); ((void)Context); // suppress warning
- return TRUE;
- }
- #endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-
- #ifndef EMULATED_THREADS_USE_NATIVE_CV
- /*
- Note:
- The implementation of condition variable is ported from Boost.Interprocess
- See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp
- */
- static void impl_cond_do_signal(cnd_t *cond, int broadcast)
- {
- int nsignal = 0;
-
- EnterCriticalSection(&cond->monitor);
- if (cond->to_unblock != 0) {
- if (cond->blocked == 0) {
- LeaveCriticalSection(&cond->monitor);
- return;
- }
- if (broadcast) {
- cond->to_unblock += nsignal = cond->blocked;
- cond->blocked = 0;
- } else {
- nsignal = 1;
- cond->to_unblock++;
- cond->blocked--;
- }
- } else if (cond->blocked > cond->gone) {
- WaitForSingleObject(cond->sem_gate, INFINITE);
- if (cond->gone != 0) {
- cond->blocked -= cond->gone;
- cond->gone = 0;
- }
- if (broadcast) {
- nsignal = cond->to_unblock = cond->blocked;
- cond->blocked = 0;
- } else {
- nsignal = cond->to_unblock = 1;
- cond->blocked--;
- }
- }
- LeaveCriticalSection(&cond->monitor);
-
- if (0 < nsignal)
- ReleaseSemaphore(cond->sem_queue, nsignal, NULL);
- }
-
- static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
- {
- int nleft = 0;
- int ngone = 0;
- int timeout = 0;
- DWORD w;
-
- WaitForSingleObject(cond->sem_gate, INFINITE);
- cond->blocked++;
- ReleaseSemaphore(cond->sem_gate, 1, NULL);
-
- mtx_unlock(mtx);
-
- w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE);
- timeout = (w == WAIT_TIMEOUT);
-
- EnterCriticalSection(&cond->monitor);
- if ((nleft = cond->to_unblock) != 0) {
- if (timeout) {
- if (cond->blocked != 0) {
- cond->blocked--;
- } else {
- cond->gone++;
- }
- }
- if (--cond->to_unblock == 0) {
- if (cond->blocked != 0) {
- ReleaseSemaphore(cond->sem_gate, 1, NULL);
- nleft = 0;
- }
- else if ((ngone = cond->gone) != 0) {
- cond->gone = 0;
- }
- }
- } else if (++cond->gone == INT_MAX/2) {
- WaitForSingleObject(cond->sem_gate, INFINITE);
- cond->blocked -= cond->gone;
- ReleaseSemaphore(cond->sem_gate, 1, NULL);
- cond->gone = 0;
- }
- LeaveCriticalSection(&cond->monitor);
-
- if (nleft == 1) {
- while (ngone--)
- WaitForSingleObject(cond->sem_queue, INFINITE);
- ReleaseSemaphore(cond->sem_gate, 1, NULL);
- }
-
- mtx_lock(mtx);
- return timeout ? thrd_busy : thrd_success;
- }
- #endif // ifndef EMULATED_THREADS_USE_NATIVE_CV
-
- static struct impl_tss_dtor_entry {
- tss_t key;
- tss_dtor_t dtor;
- } impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
-
- static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor)
- {
- int i;
- for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
- if (!impl_tss_dtor_tbl[i].dtor)
- break;
- }
- if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
- return 1;
- impl_tss_dtor_tbl[i].key = key;
- impl_tss_dtor_tbl[i].dtor = dtor;
- return 0;
- }
-
- static void impl_tss_dtor_invoke()
- {
- int i;
- for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
- if (impl_tss_dtor_tbl[i].dtor) {
- void* val = tss_get(impl_tss_dtor_tbl[i].key);
- if (val)
- (impl_tss_dtor_tbl[i].dtor)(val);
- }
- }
- }
-
-
- /*--------------- 7.25.2 Initialization functions ---------------*/
- // 7.25.2.1
- static inline void
- call_once(once_flag *flag, void (*func)(void))
- {
- assert(flag && func);
- #ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
- {
- struct impl_call_once_param param;
- param.func = func;
- InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL);
- }
- #else
- if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) {
- (func)();
- InterlockedExchange(&flag->status, 2);
- } else {
- while (flag->status == 1) {
- // busy loop!
- thrd_yield();
- }
- }
- #endif
- }
-
-
- /*------------- 7.25.3 Condition variable functions -------------*/
- // 7.25.3.1
- static inline int
- cnd_broadcast(cnd_t *cond)
- {
- if (!cond) return thrd_error;
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- WakeAllConditionVariable(&cond->condvar);
- #else
- impl_cond_do_signal(cond, 1);
- #endif
- return thrd_success;
- }
-
- // 7.25.3.2
- static inline void
- cnd_destroy(cnd_t *cond)
- {
- assert(cond);
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- // do nothing
- #else
- CloseHandle(cond->sem_queue);
- CloseHandle(cond->sem_gate);
- DeleteCriticalSection(&cond->monitor);
- #endif
- }
-
- // 7.25.3.3
- static inline int
- cnd_init(cnd_t *cond)
- {
- if (!cond) return thrd_error;
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- InitializeConditionVariable(&cond->condvar);
- #else
- cond->blocked = 0;
- cond->gone = 0;
- cond->to_unblock = 0;
- cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
- cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL);
- InitializeCriticalSection(&cond->monitor);
- #endif
- return thrd_success;
- }
-
- // 7.25.3.4
- static inline int
- cnd_signal(cnd_t *cond)
- {
- if (!cond) return thrd_error;
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- WakeConditionVariable(&cond->condvar);
- #else
- impl_cond_do_signal(cond, 0);
- #endif
- return thrd_success;
- }
-
- // 7.25.3.5
- static inline int
- cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
- {
- if (!cond || !mtx || !xt) return thrd_error;
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt)))
- return thrd_success;
- return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error;
- #else
- return impl_cond_do_wait(cond, mtx, xt);
- #endif
- }
-
- // 7.25.3.6
- static inline int
- cnd_wait(cnd_t *cond, mtx_t *mtx)
- {
- if (!cond || !mtx) return thrd_error;
- #ifdef EMULATED_THREADS_USE_NATIVE_CV
- SleepConditionVariableCS(&cond->condvar, mtx, INFINITE);
- #else
- impl_cond_do_wait(cond, mtx, NULL);
- #endif
- return thrd_success;
- }
-
-
- /*-------------------- 7.25.4 Mutex functions --------------------*/
- // 7.25.4.1
- static inline void
- mtx_destroy(mtx_t *mtx)
- {
- assert(mtx);
- DeleteCriticalSection(mtx);
- }
-
- // 7.25.4.2
- static inline int
- mtx_init(mtx_t *mtx, int type)
- {
- if (!mtx) return thrd_error;
- if (type != mtx_plain && type != mtx_timed && type != mtx_try
- && type != (mtx_plain|mtx_recursive)
- && type != (mtx_timed|mtx_recursive)
- && type != (mtx_try|mtx_recursive))
- return thrd_error;
- InitializeCriticalSection(mtx);
- return thrd_success;
- }
-
- // 7.25.4.3
- static inline int
- mtx_lock(mtx_t *mtx)
- {
- if (!mtx) return thrd_error;
- EnterCriticalSection(mtx);
- return thrd_success;
- }
-
- // 7.25.4.4
- static inline int
- mtx_timedlock(mtx_t *mtx, const xtime *xt)
- {
- time_t expire, now;
- if (!mtx || !xt) return thrd_error;
- expire = time(NULL);
- expire += xt->sec;
- while (mtx_trylock(mtx) != thrd_success) {
- now = time(NULL);
- if (expire < now)
- return thrd_busy;
- // busy loop!
- thrd_yield();
- }
- return thrd_success;
- }
-
- // 7.25.4.5
- static inline int
- mtx_trylock(mtx_t *mtx)
- {
- if (!mtx) return thrd_error;
- return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;
- }
-
- // 7.25.4.6
- static inline int
- mtx_unlock(mtx_t *mtx)
- {
- if (!mtx) return thrd_error;
- LeaveCriticalSection(mtx);
- return thrd_success;
- }
-
-
- /*------------------- 7.25.5 Thread functions -------------------*/
- // 7.25.5.1
- static inline int
- thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
- {
- struct impl_thrd_param *pack;
- uintptr_t handle;
- if (!thr) return thrd_error;
- pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param));
- if (!pack) return thrd_nomem;
- pack->func = func;
- pack->arg = arg;
- handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
- if (handle == 0) {
- if (errno == EAGAIN || errno == EACCES)
- return thrd_nomem;
- return thrd_error;
- }
- *thr = (thrd_t)handle;
- return thrd_success;
- }
-
- #if 0
- // 7.25.5.2
- static inline thrd_t
- thrd_current(void)
- {
- HANDLE hCurrentThread;
- BOOL bRet;
-
- /* GetCurrentThread() returns a pseudo-handle, which is useless. We need
- * to call DuplicateHandle to get a real handle. However the handle value
- * will not match the one returned by thread_create.
- *
- * Other potential solutions would be:
- * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations
- * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread.
- *
- * Neither is particularly nice.
- *
- * Life would be much easier if C11 threads had different abstractions for
- * threads and thread IDs, just like C++11 threads does...
- */
-
- bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
- GetCurrentThread(), // source (pseudo) handle
- GetCurrentProcess(), // target process
- &hCurrentThread, // target handle
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS);
- assert(bRet);
- if (!bRet) {
- hCurrentThread = GetCurrentThread();
- }
- return hCurrentThread;
- }
- #endif
-
- // 7.25.5.3
- static inline int
- thrd_detach(thrd_t thr)
- {
- CloseHandle(thr);
- return thrd_success;
- }
-
- // 7.25.5.4
- static inline int
- thrd_equal(thrd_t thr0, thrd_t thr1)
- {
- return GetThreadId(thr0) == GetThreadId(thr1);
- }
-
- // 7.25.5.5
- static inline void
- thrd_exit(int res)
- {
- impl_tss_dtor_invoke();
- _endthreadex((unsigned)res);
- }
-
- // 7.25.5.6
- static inline int
- thrd_join(thrd_t thr, int *res)
- {
- DWORD w, code;
- w = WaitForSingleObject(thr, INFINITE);
- if (w != WAIT_OBJECT_0)
- return thrd_error;
- if (res) {
- if (!GetExitCodeThread(thr, &code)) {
- CloseHandle(thr);
- return thrd_error;
- }
- *res = (int)code;
- }
- CloseHandle(thr);
- return thrd_success;
- }
-
- // 7.25.5.7
- static inline void
- thrd_sleep(const xtime *xt)
- {
- assert(xt);
- Sleep(impl_xtime2msec(xt));
- }
-
- // 7.25.5.8
- static inline void
- thrd_yield(void)
- {
- SwitchToThread();
- }
-
-
- /*----------- 7.25.6 Thread-specific storage functions -----------*/
- // 7.25.6.1
- static inline int
- tss_create(tss_t *key, tss_dtor_t dtor)
- {
- if (!key) return thrd_error;
- *key = TlsAlloc();
- if (dtor) {
- if (impl_tss_dtor_register(*key, dtor)) {
- TlsFree(*key);
- return thrd_error;
- }
- }
- return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
- }
-
- // 7.25.6.2
- static inline void
- tss_delete(tss_t key)
- {
- TlsFree(key);
- }
-
- // 7.25.6.3
- static inline void *
- tss_get(tss_t key)
- {
- return TlsGetValue(key);
- }
-
- // 7.25.6.4
- static inline int
- tss_set(tss_t key, void *val)
- {
- return TlsSetValue(key, val) ? thrd_success : thrd_error;
- }
-
-
- /*-------------------- 7.25.7 Time functions --------------------*/
- // 7.25.6.1
- static inline int
- xtime_get(xtime *xt, int base)
- {
- if (!xt) return 0;
- if (base == TIME_UTC) {
- xt->sec = time(NULL);
- xt->nsec = 0;
- return base;
- }
- return 0;
- }
|