Last active
September 25, 2024 12:28
-
-
Save yohhoy/2223710 to your computer and use it in GitHub Desktop.
C11 <threads.h> emulation library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* C11 <threads.h> emulation library | |
* | |
* (C) Copyright yohhoy 2012. | |
* Distributed under the Boost Software License, Version 1.0. | |
* (See copy at http://www.boost.org/LICENSE_1_0.txt) | |
*/ | |
#ifndef EMULATED_THREADS_H_INCLUDED_ | |
#define EMULATED_THREADS_H_INCLUDED_ | |
#include <time.h> | |
#if defined(_WIN32) && defined(_MSC_VER) | |
#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 | |
/*---------------------------- 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 struct mtx_t { | |
CRITICAL_SECTION cs; | |
} 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 | |
#elif defined(__unix__) || defined(__unix) | |
#include <pthread.h> | |
/*---------------------------- macros ----------------------------*/ | |
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT | |
#ifdef INIT_ONCE_STATIC_INIT | |
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS | |
#else | |
#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once. | |
#endif | |
/*---------------------------- types ----------------------------*/ | |
typedef pthread_cond_t cnd_t; | |
typedef pthread_t thrd_t; | |
typedef pthread_key_t tss_t; | |
typedef pthread_mutex_t mtx_t; | |
typedef pthread_once_t once_flag; | |
#else | |
#error Not supported on this platform. | |
#endif | |
/*---------------------------- types ----------------------------*/ | |
typedef void (*tss_dtor_t)(void*); | |
typedef int (*thrd_start_t)(void*); | |
struct xtime { | |
time_t sec; | |
long nsec; | |
}; | |
typedef struct xtime xtime; | |
/*-------------------- enumeration constants --------------------*/ | |
enum { | |
mtx_plain = 0, | |
mtx_try = 1, | |
mtx_timed = 2, | |
mtx_recursive = 4 | |
}; | |
enum { | |
thrd_success = 0, // succeeded | |
thrd_timeout, // timeout | |
thrd_error, // failed | |
thrd_busy, // resource busy | |
thrd_nomem // out of memory | |
}; | |
/*-------------------------- functions --------------------------*/ | |
void call_once(once_flag *flag, void (*func)(void)); | |
int cnd_broadcast(cnd_t *cond); | |
void cnd_destroy(cnd_t *cond); | |
int cnd_init(cnd_t *cond); | |
int cnd_signal(cnd_t *cond); | |
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt); | |
int cnd_wait(cnd_t *cond, mtx_t *mtx); | |
void mtx_destroy(mtx_t *mtx); | |
int mtx_init(mtx_t *mtx, int type); | |
int mtx_lock(mtx_t *mtx); | |
int mtx_timedlock(mtx_t *mtx, const xtime *xt); | |
int mtx_trylock(mtx_t *mtx); | |
int mtx_unlock(mtx_t *mtx); | |
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); | |
thrd_t thrd_current(void); | |
int thrd_detach(thrd_t thr); | |
int thrd_equal(thrd_t thr0, thrd_t thr1); | |
void thrd_exit(int res); | |
int thrd_join(thrd_t thr, int *res); | |
void thrd_sleep(const xtime *xt); | |
void thrd_yield(void); | |
int tss_create(tss_t *key, tss_dtor_t dtor); | |
void tss_delete(tss_t key); | |
void *tss_get(tss_t key); | |
int tss_set(tss_t key, void *val); | |
int xtime_get(xtime *xt, int base); | |
enum { TIME_UTC = 1 }; | |
#endif /* EMULATED_THREADS_H_INCLUDED_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* C11 <threads.h> emulation library | |
* | |
* (C) Copyright yohhoy 2012. | |
* Distributed under the Boost Software License, Version 1.0. | |
* (See copy at http://www.boost.org/LICENSE_1_0.txt) | |
*/ | |
#include <assert.h> | |
#include <limits.h> | |
#include <errno.h> | |
#include <process.h> // MSVCRT | |
/* | |
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. | |
*/ | |
#if _WIN32_WINNT >= 0x0600 | |
// Prefer native WindowsAPI on newer environment. | |
#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE | |
#define EMULATED_THREADS_USE_NATIVE_CV | |
#endif | |
#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE | |
#include "threads.h" | |
/* | |
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(); // 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 / 1000000)); | |
} | |
#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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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->cs, 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 | |
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->cs, INFINITE); | |
#else | |
impl_cond_do_wait(cond, mtx, NULL); | |
#endif | |
return thrd_success; | |
} | |
/*-------------------- 7.25.4 Mutex functions --------------------*/ | |
// 7.25.4.1 | |
void mtx_destroy(mtx_t *mtx) | |
{ | |
assert(mtx); | |
DeleteCriticalSection(&mtx->cs); | |
} | |
// 7.25.4.2 | |
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->cs); | |
return thrd_success; | |
} | |
// 7.25.4.3 | |
int mtx_lock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
EnterCriticalSection(&mtx->cs); | |
return thrd_success; | |
} | |
// 7.25.4.4 | |
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 | |
int mtx_trylock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
return TryEnterCriticalSection(&mtx->cs) ? thrd_success : thrd_busy; | |
} | |
// 7.25.4.6 | |
int mtx_unlock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
LeaveCriticalSection(&mtx->cs); | |
return thrd_success; | |
} | |
/*------------------- 7.25.5 Thread functions -------------------*/ | |
// 7.25.5.1 | |
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 = 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; | |
} | |
// 7.25.5.2 | |
thrd_t thrd_current(void) | |
{ | |
return GetCurrentThread(); | |
} | |
// 7.25.5.3 | |
int thrd_detach(thrd_t thr) | |
{ | |
CloseHandle(thr); | |
return thrd_success; | |
} | |
// 7.25.5.4 | |
int thrd_equal(thrd_t thr0, thrd_t thr1) | |
{ | |
return (thr0 == thr1); | |
} | |
// 7.25.5.5 | |
void thrd_exit(int res) | |
{ | |
impl_tss_dtor_invoke(); | |
_endthreadex((unsigned)res); | |
} | |
// 7.25.5.6 | |
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 | |
void thrd_sleep(const xtime *xt) | |
{ | |
assert(xt); | |
Sleep(impl_xtime2msec(xt)); | |
} | |
// 7.25.5.8 | |
void thrd_yield(void) | |
{ | |
SwitchToThread(); | |
} | |
/*----------- 7.25.6 Thread-specific storage functions -----------*/ | |
// 7.25.6.1 | |
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 | |
void tss_delete(tss_t key) | |
{ | |
TlsFree(key); | |
} | |
// 7.25.6.3 | |
void *tss_get(tss_t key) | |
{ | |
return TlsGetValue(key); | |
} | |
// 7.25.6.4 | |
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 | |
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* C11 <threads.h> emulation library | |
* | |
* (C) Copyright yohhoy 2012. | |
* Distributed under the Boost Software License, Version 1.0. | |
* (See copy at http://www.boost.org/LICENSE_1_0.txt) | |
*/ | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <limits.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <sched.h> | |
/* | |
Configuration macro: | |
EMULATED_THREADS_USE_NATIVE_TIMEDLOCK | |
Use pthread_mutex_timedlock() for `mtx_timedlock()' | |
Otherwise use mtx_trylock() + *busy loop* emulation. | |
*/ | |
#if !defined(__CYGWIN__) | |
#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK | |
#endif | |
#include "threads.h" | |
/* | |
Implementation limits: | |
- Conditionally emulation for "mutex with timeout" | |
(see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) | |
*/ | |
struct impl_thrd_param { | |
thrd_start_t func; | |
void *arg; | |
}; | |
void *impl_thrd_routine(void *p) | |
{ | |
struct impl_thrd_param pack = *((struct impl_thrd_param *)p); | |
free(p); | |
return (void*)pack.func(pack.arg); | |
} | |
/*--------------- 7.25.2 Initialization functions ---------------*/ | |
// 7.25.2.1 | |
void call_once(once_flag *flag, void (*func)(void)) | |
{ | |
pthread_once(flag, func); | |
} | |
/*------------- 7.25.3 Condition variable functions -------------*/ | |
// 7.25.3.1 | |
int cnd_broadcast(cnd_t *cond) | |
{ | |
if (!cond) return thrd_error; | |
pthread_cond_broadcast(cond); | |
return thrd_success; | |
} | |
// 7.25.3.2 | |
void cnd_destroy(cnd_t *cond) | |
{ | |
assert(cond); | |
pthread_cond_destroy(cond); | |
} | |
// 7.25.3.3 | |
int cnd_init(cnd_t *cond) | |
{ | |
if (!cond) return thrd_error; | |
pthread_cond_init(cond, NULL); | |
return thrd_success; | |
} | |
// 7.25.3.4 | |
int cnd_signal(cnd_t *cond) | |
{ | |
if (!cond) return thrd_error; | |
pthread_cond_signal(cond); | |
return thrd_success; | |
} | |
// 7.25.3.5 | |
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) | |
{ | |
struct timespec abs_time; | |
int rt; | |
if (!cond || !mtx || !xt) return thrd_error; | |
rt = pthread_cond_timedwait(cond, mtx, &abs_time); | |
if (rt == ETIMEDOUT) | |
return thrd_busy; | |
return (rt == 0) ? thrd_success : thrd_error; | |
} | |
// 7.25.3.6 | |
int cnd_wait(cnd_t *cond, mtx_t *mtx) | |
{ | |
if (!cond || !mtx) return thrd_error; | |
pthread_cond_wait(cond, mtx); | |
return thrd_success; | |
} | |
/*-------------------- 7.25.4 Mutex functions --------------------*/ | |
// 7.25.4.1 | |
void mtx_destroy(mtx_t *mtx) | |
{ | |
assert(mtx); | |
pthread_mutex_destroy(mtx); | |
} | |
// 7.25.4.2 | |
int mtx_init(mtx_t *mtx, int type) | |
{ | |
pthread_mutexattr_t attr; | |
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; | |
pthread_mutexattr_init(&attr); | |
if ((type & mtx_recursive) != 0) { | |
#if defined(__linux__) || defined(__linux) | |
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); | |
#else | |
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | |
#endif | |
} | |
pthread_mutex_init(mtx, &attr); | |
pthread_mutexattr_destroy(&attr); | |
return thrd_success; | |
} | |
// 7.25.4.3 | |
int mtx_lock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
pthread_mutex_lock(mtx); | |
return thrd_success; | |
} | |
// 7.25.4.4 | |
int mtx_timedlock(mtx_t *mtx, const xtime *xt) | |
{ | |
if (!mtx || !xt) return thrd_error; | |
{ | |
#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK | |
struct timespec ts; | |
int rt; | |
ts.tv_sec = xt->sec; | |
ts.tv_nsec = xt->nsec; | |
rt = pthread_mutex_timedlock(mtx, &ts); | |
if (rt == 0) | |
return thrd_success; | |
return (rt == ETIMEDOUT) ? thrd_busy : thrd_error; | |
#else | |
time_t expire = time(NULL); | |
expire += xt->sec; | |
while (mtx_trylock(mtx) != thrd_success) { | |
time_t now = time(NULL); | |
if (expire < now) | |
return thrd_busy; | |
// busy loop! | |
thrd_yield(); | |
} | |
return thrd_success; | |
#endif | |
} | |
} | |
// 7.25.4.5 | |
int mtx_trylock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; | |
} | |
// 7.25.4.6 | |
int mtx_unlock(mtx_t *mtx) | |
{ | |
if (!mtx) return thrd_error; | |
pthread_mutex_unlock(mtx); | |
return thrd_success; | |
} | |
/*------------------- 7.25.5 Thread functions -------------------*/ | |
// 7.25.5.1 | |
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) | |
{ | |
struct impl_thrd_param *pack; | |
if (!thr) return thrd_error; | |
pack = malloc(sizeof(struct impl_thrd_param)); | |
if (!pack) return thrd_nomem; | |
pack->func = func; | |
pack->arg = arg; | |
if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { | |
free(pack); | |
return thrd_error; | |
} | |
return thrd_success; | |
} | |
// 7.25.5.2 | |
thrd_t thrd_current(void) | |
{ | |
return pthread_self(); | |
} | |
// 7.25.5.3 | |
int thrd_detach(thrd_t thr) | |
{ | |
return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; | |
} | |
// 7.25.5.4 | |
int thrd_equal(thrd_t thr0, thrd_t thr1) | |
{ | |
return pthread_equal(thr0, thr1); | |
} | |
// 7.25.5.5 | |
void thrd_exit(int res) | |
{ | |
pthread_exit((void*)res); | |
} | |
// 7.25.5.6 | |
int thrd_join(thrd_t thr, int *res) | |
{ | |
void *code; | |
if (pthread_join(thr, &code) != 0) | |
return thrd_error; | |
if (res) | |
*res = (int)code; | |
return thrd_success; | |
} | |
// 7.25.5.7 | |
void thrd_sleep(const xtime *xt) | |
{ | |
struct timespec req; | |
assert(xt); | |
req.tv_sec = xt->sec; | |
req.tv_nsec = xt->nsec; | |
nanosleep(&req, NULL); | |
} | |
// 7.25.5.8 | |
void thrd_yield(void) | |
{ | |
sched_yield(); | |
} | |
/*----------- 7.25.6 Thread-specific storage functions -----------*/ | |
// 7.25.6.1 | |
int tss_create(tss_t *key, tss_dtor_t dtor) | |
{ | |
if (!key) return thrd_error; | |
return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; | |
} | |
// 7.25.6.2 | |
void tss_delete(tss_t key) | |
{ | |
pthread_key_delete(key); | |
} | |
// 7.25.6.3 | |
void *tss_get(tss_t key) | |
{ | |
return pthread_getspecific(key); | |
} | |
// 7.25.6.4 | |
int tss_set(tss_t key, void *val) | |
{ | |
return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; | |
} | |
/*-------------------- 7.25.7 Time functions --------------------*/ | |
// 7.25.6.1 | |
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; | |
} |
At least one bug.
In threads_msvc.c:
static DWORD impl_xtime2msec(const xtime *xt)
{
return (DWORD)((xt->sec * 1000u) + (xt->nsec / 1000));
}
should be
static DWORD impl_xtime2msec(const xtime *xt)
{
return (DWORD)((xt->sec * 1000u) + (xt->nsec / 1000000));
}
because nsec is nanoseconds.
Arnaud
Seems like we're asserting when either the flag or the func is non null.
void call_once(once_flag *flag, void (*func)(void))
{
assert(!flag && !func);
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
Should we assert when either one is null ?
void call_once(once_flag *flag, void (*func)(void))
{
assert(flag && func);
#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
This code snippet is used in Mesa 3D project. If you need a stable code, please refer to http://cgit.freedesktop.org/mesa/mesa/log/include/c11
(I fixed small bugs which @Arnaud92 and @evelikov points out, thanks!)
What is the difference between this and tinycthreads?
Hi yohhoy, do you accept switch the license of the files in http://cgit.freedesktop.org/mesa/mesa/log/include/c11 from Boost to MIT?
Rationale: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15497
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
#ifdef INIT_ONCE_STATIC_INIT
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
#else
#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once.
#endif
Why use INIT_ONCE_STATIC_INIT? Maybe it's should be PTHREAD_DESTRUCTOR_ITERATIONS
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
テストしてない! Untested!