This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
[*.{c,h}]
indent_style = space
indent_size = 4
+32
View File
@@ -0,0 +1,32 @@
# Copyright © 2022 Yonggang Luo
# SPDX-License-Identifier: MIT
inc_util_c11 = [include_directories('../..')]
files_mesa_util_c11 = files(
'time.c',
)
if with_c11_threads
# no need implement c11 threads
elif host_machine.system() == 'windows'
files_mesa_util_c11 += 'threads_win32.c'
files_mesa_util_c11 += 'threads_win32_tls_callback.cpp'
else
files_mesa_util_c11 += 'threads_posix.c'
endif
_libmesa_util_c11 = static_library(
'mesa_util_c11',
[files_mesa_util_c11],
include_directories : inc_util_c11,
c_args : [c_msvc_compat_args],
gnu_symbol_visibility : 'hidden',
build_by_default : false
)
idep_mesautilc11 = declare_dependency(
link_with : _libmesa_util_c11,
include_directories : inc_util_c11,
dependencies : [dep_clock, dep_thread, dep_atomic, dep_m, dep_valgrind],
)
+347
View File
@@ -0,0 +1,347 @@
/*
* SPDX-License-Identifier: BSL-1.0
* Copyright yohhoy 2012.
*/
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <sched.h>
#include <stdint.h> /* for intptr_t */
#include "c11/threads.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__) && !defined(__APPLE__) && !defined(__NetBSD__)
#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
#endif
/*---------------------------- types ----------------------------*/
/*
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;
};
static void *
impl_thrd_routine(void *p)
{
struct impl_thrd_param pack = *((struct impl_thrd_param *)p);
free(p);
return (void*)(intptr_t)pack.func(pack.arg);
}
/*--------------- 7.25.2 Initialization functions ---------------*/
// 7.25.2.1
#ifndef __once_flag_defined
void
call_once(once_flag *flag, void (*func)(void))
{
pthread_once(flag, func);
}
#endif
/*------------- 7.25.3 Condition variable functions -------------*/
// 7.25.3.1
int
cnd_broadcast(cnd_t *cond)
{
assert(cond != NULL);
return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error;
}
// 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)
{
assert(cond != NULL);
return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error;
}
// 7.25.3.4
int
cnd_signal(cnd_t *cond)
{
assert(cond != NULL);
return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
}
// 7.25.3.5
int
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
{
int rt;
assert(mtx != NULL);
assert(cond != NULL);
assert(abs_time != NULL);
rt = pthread_cond_timedwait(cond, mtx, abs_time);
if (rt == ETIMEDOUT)
return thrd_timedout;
return (rt == 0) ? thrd_success : thrd_error;
}
// 7.25.3.6
int
cnd_wait(cnd_t *cond, mtx_t *mtx)
{
assert(mtx != NULL);
assert(cond != NULL);
return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error;
}
/*-------------------- 7.25.4 Mutex functions --------------------*/
// 7.25.4.1
void
mtx_destroy(mtx_t *mtx)
{
assert(mtx != NULL);
pthread_mutex_destroy(mtx);
}
/*
* XXX: Workaround when building with -O0 and without pthreads link.
*
* In such cases constant folding and dead code elimination won't be
* available, thus the compiler will always add the pthread_mutexattr*
* functions into the binary. As we try to link, we'll fail as the
* symbols are unresolved.
*
* Ideally we'll enable the optimisations locally, yet that does not
* seem to work.
*
* So the alternative workaround is to annotate the symbols as weak.
* Thus the linker will be happy and things don't clash when building
* with -O1 or greater.
*/
#if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
__attribute__((weak))
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
__attribute__((weak))
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
__attribute__((weak))
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
#endif
// 7.25.4.2
int
mtx_init(mtx_t *mtx, int type)
{
pthread_mutexattr_t attr;
assert(mtx != NULL);
if (type != mtx_plain && type != mtx_timed
&& type != (mtx_plain|mtx_recursive)
&& type != (mtx_timed|mtx_recursive))
return thrd_error;
if ((type & mtx_recursive) == 0) {
pthread_mutex_init(mtx, NULL);
return thrd_success;
}
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(mtx, &attr);
pthread_mutexattr_destroy(&attr);
return thrd_success;
}
// 7.25.4.3
int
mtx_lock(mtx_t *mtx)
{
assert(mtx != NULL);
return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
}
static int
threads_timespec_compare(const struct timespec *a, const struct timespec *b)
{
if (a->tv_sec < b->tv_sec) {
return -1;
} else if (a->tv_sec > b->tv_sec) {
return 1;
} else if (a->tv_nsec < b->tv_nsec) {
return -1;
} else if (a->tv_nsec > b->tv_nsec) {
return 1;
}
return 0;
}
// 7.25.4.4
int
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
{
assert(mtx != NULL);
assert(ts != NULL);
{
#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
int rt;
rt = pthread_mutex_timedlock(mtx, ts);
if (rt == 0)
return thrd_success;
return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error;
#else
while (mtx_trylock(mtx) != thrd_success) {
struct timespec now;
if (timespec_get(&now, TIME_UTC) != TIME_UTC) {
return thrd_error;
}
if (threads_timespec_compare(ts, &now) < 0)
return thrd_timedout;
// busy loop!
thrd_yield();
}
return thrd_success;
#endif
}
}
// 7.25.4.5
int
mtx_trylock(mtx_t *mtx)
{
assert(mtx != NULL);
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
}
// 7.25.4.6
int
mtx_unlock(mtx_t *mtx)
{
assert(mtx != NULL);
return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
}
/*------------------- 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;
assert(thr != NULL);
pack = (struct impl_thrd_param *)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
_Noreturn
void
thrd_exit(int res)
{
pthread_exit((void*)(intptr_t)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)(intptr_t)code;
return thrd_success;
}
// 7.25.5.7
int
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
{
assert(time_point != NULL);
return nanosleep(time_point, remaining);
}
// 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)
{
assert(key != NULL);
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;
}
+425
View File
@@ -0,0 +1,425 @@
/*
* SPDX-License-Identifier: BSL-1.0
* Copyright yohhoy 2012.
*/
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <process.h> // MSVCRT
#include <stdlib.h>
#include <stdbool.h>
#include "c11/threads.h"
#include "threads_win32.h"
#include <windows.h>
/*
Configuration macro:
EMULATED_THREADS_TSS_DTOR_SLOTNUM
Max registerable TSS dtor number.
*/
#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE
static_assert(sizeof(cnd_t) == sizeof(CONDITION_VARIABLE), "The size of cnd_t must equal to CONDITION_VARIABLE");
static_assert(sizeof(thrd_t) == sizeof(HANDLE), "The size of thrd_t must equal to HANDLE");
static_assert(sizeof(tss_t) == sizeof(DWORD), "The size of tss_t must equal to DWORD");
static_assert(sizeof(mtx_t) == sizeof(CRITICAL_SECTION), "The size of mtx_t must equal to CRITICAL_SECTION");
static_assert(sizeof(once_flag) == sizeof(INIT_ONCE), "The size of once_flag must equal to INIT_ONCE");
/*
Implementation limits:
- Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
*/
struct impl_thrd_param {
thrd_start_t func;
void *arg;
thrd_t thrd;
};
struct thrd_state {
thrd_t thrd;
bool handle_need_close;
};
static thread_local struct thrd_state impl_current_thread = { 0 };
static unsigned __stdcall impl_thrd_routine(void *p)
{
struct impl_thrd_param *pack_p = (struct impl_thrd_param *)p;
struct impl_thrd_param pack;
int code;
impl_current_thread.thrd = pack_p->thrd;
impl_current_thread.handle_need_close = false;
memcpy(&pack, pack_p, sizeof(struct impl_thrd_param));
free(p);
code = pack.func(pack.arg);
return (unsigned)code;
}
static time_t impl_timespec2msec(const struct timespec *ts)
{
return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
}
static DWORD impl_abs2relmsec(const struct timespec *abs_time)
{
const time_t abs_ms = impl_timespec2msec(abs_time);
struct timespec now;
timespec_get(&now, TIME_UTC);
const time_t now_ms = impl_timespec2msec(&now);
const DWORD rel_ms = (abs_ms > now_ms) ? (DWORD)(abs_ms - now_ms) : 0;
return rel_ms;
}
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;
}
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(void)
{
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);
struct impl_call_once_param param;
param.func = func;
InitOnceExecuteOnce((PINIT_ONCE)flag, impl_call_once_callback, (PVOID)&param, NULL);
}
/*------------- 7.25.3 Condition variable functions -------------*/
// 7.25.3.1
int
cnd_broadcast(cnd_t *cond)
{
assert(cond != NULL);
WakeAllConditionVariable((PCONDITION_VARIABLE)cond);
return thrd_success;
}
// 7.25.3.2
void
cnd_destroy(cnd_t *cond)
{
(void)cond;
assert(cond != NULL);
// do nothing
}
// 7.25.3.3
int
cnd_init(cnd_t *cond)
{
assert(cond != NULL);
InitializeConditionVariable((PCONDITION_VARIABLE)cond);
return thrd_success;
}
// 7.25.3.4
int
cnd_signal(cnd_t *cond)
{
assert(cond != NULL);
WakeConditionVariable((PCONDITION_VARIABLE)cond);
return thrd_success;
}
// 7.25.3.5
int
cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *abs_time)
{
assert(cond != NULL);
assert(mtx != NULL);
assert(abs_time != NULL);
const DWORD timeout = impl_abs2relmsec(abs_time);
if (SleepConditionVariableCS((PCONDITION_VARIABLE)cond, (PCRITICAL_SECTION)mtx, timeout))
return thrd_success;
return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error;
}
// 7.25.3.6
int
cnd_wait(cnd_t *cond, mtx_t *mtx)
{
assert(cond != NULL);
assert(mtx != NULL);
SleepConditionVariableCS((PCONDITION_VARIABLE)cond, (PCRITICAL_SECTION)mtx, INFINITE);
return thrd_success;
}
/*-------------------- 7.25.4 Mutex functions --------------------*/
// 7.25.4.1
void
mtx_destroy(mtx_t *mtx)
{
assert(mtx);
DeleteCriticalSection((PCRITICAL_SECTION)mtx);
}
// 7.25.4.2
int
mtx_init(mtx_t *mtx, int type)
{
assert(mtx != NULL);
if (type != mtx_plain && type != mtx_timed
&& type != (mtx_plain|mtx_recursive)
&& type != (mtx_timed|mtx_recursive))
return thrd_error;
InitializeCriticalSection((PCRITICAL_SECTION)mtx);
return thrd_success;
}
// 7.25.4.3
int
mtx_lock(mtx_t *mtx)
{
assert(mtx != NULL);
EnterCriticalSection((PCRITICAL_SECTION)mtx);
return thrd_success;
}
// 7.25.4.4
int
mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
{
assert(mtx != NULL);
assert(ts != NULL);
while (mtx_trylock(mtx) != thrd_success) {
if (impl_abs2relmsec(ts) == 0)
return thrd_timedout;
// busy loop!
thrd_yield();
}
return thrd_success;
}
// 7.25.4.5
int
mtx_trylock(mtx_t *mtx)
{
assert(mtx != NULL);
return TryEnterCriticalSection((PCRITICAL_SECTION)mtx) ? thrd_success : thrd_busy;
}
// 7.25.4.6
int
mtx_unlock(mtx_t *mtx)
{
assert(mtx != NULL);
LeaveCriticalSection((PCRITICAL_SECTION)mtx);
return thrd_success;
}
void
__threads_win32_tls_callback(void)
{
struct thrd_state *state = &impl_current_thread;
impl_tss_dtor_invoke();
if (state->handle_need_close) {
state->handle_need_close = false;
CloseHandle(state->thrd.handle);
}
}
/*------------------- 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;
assert(thr != NULL);
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, CREATE_SUSPENDED, NULL);
if (handle == 0) {
free(pack);
if (errno == EAGAIN || errno == EACCES)
return thrd_nomem;
return thrd_error;
}
thr->handle = (void*)handle;
pack->thrd = *thr;
ResumeThread((HANDLE)handle);
return thrd_success;
}
// 7.25.5.2
thrd_t
thrd_current(void)
{
/* GetCurrentThread() returns a pseudo-handle, which we need
* to pass to DuplicateHandle(). Only the resulting handle can be used
* from other threads.
*
* Note that neither handle can be compared to the one by thread_create.
* Only the thread IDs - as returned by GetThreadId() and GetCurrentThreadId()
* can be compared directly.
*
* 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...
*/
struct thrd_state *state = &impl_current_thread;
if (state->thrd.handle == NULL)
{
if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
&(state->thrd.handle), 0, false, DUPLICATE_SAME_ACCESS))
{
abort();
}
state->handle_need_close = true;
}
return state->thrd;
}
// 7.25.5.3
int
thrd_detach(thrd_t thr)
{
CloseHandle(thr.handle);
return thrd_success;
}
// 7.25.5.4
int
thrd_equal(thrd_t thr0, thrd_t thr1)
{
return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
}
// 7.25.5.5
_Noreturn
void
thrd_exit(int res)
{
_endthreadex((unsigned)res);
}
// 7.25.5.6
int
thrd_join(thrd_t thr, int *res)
{
DWORD w, code;
if (thr.handle == NULL) {
return thrd_error;
}
w = WaitForSingleObject(thr.handle, INFINITE);
if (w != WAIT_OBJECT_0)
return thrd_error;
if (res) {
if (!GetExitCodeThread(thr.handle, &code)) {
CloseHandle(thr.handle);
return thrd_error;
}
*res = (int)code;
}
CloseHandle(thr.handle);
return thrd_success;
}
// 7.25.5.7
int
thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
{
(void)remaining;
assert(time_point);
assert(!remaining); /* not implemented */
Sleep((DWORD)impl_timespec2msec(time_point));
return 0;
}
// 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)
{
assert(key != NULL);
*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;
}
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2022 Yonggang Luo
* SPDX-License-Identifier: MIT
*/
#ifndef C11_IMPL_THREADS_WIN32_H_
#define C11_IMPL_THREADS_WIN32_H_
#ifdef __cplusplus
extern "C" {
#endif
void __threads_win32_tls_callback(void);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,18 @@
/*
* Copyright 2022 Yonggang Luo
* SPDX-License-Identifier: MIT
*/
#include "threads_win32.h"
struct tls_callback
{
tls_callback()
{
}
~tls_callback()
{
__threads_win32_tls_callback();
}
};
static thread_local tls_callback tls_callback_instance;
+138
View File
@@ -0,0 +1,138 @@
/*
* C11 <time.h> implementation
*
* (C) Copyright yohhoy 2012.
* Copyright 2022 Yonggang Luo
* 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.
*/
#include "c11/time.h"
#ifdef _TIMESPEC_GET_NEED_IMPL
#if defined(_WIN32) && !defined(HAVE_PTHREAD)
#include "c11/threads.h"
#include <windows.h>
static LARGE_INTEGER frequency;
static
void
c23_timespec_get_init(void)
{
QueryPerformanceFrequency(&frequency);
}
int
c23_timespec_get(struct timespec *ts, int base)
{
/* difference between 1970 and 1601 */
#define _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
/* 1 tick is 100 nanoseconds */
#define _TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull
if (!ts)
return 0;
if (base == TIME_UTC) {
FILETIME ft;
ULARGE_INTEGER date;
LONGLONG ticks;
GetSystemTimeAsFileTime(&ft);
date.HighPart = ft.dwHighDateTime;
date.LowPart = ft.dwLowDateTime;
ticks = (LONGLONG)(date.QuadPart - _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS);
ts->tv_sec = ticks / _TIMESPEC_IMPL_TICKS_PER_SECONDS;
ts->tv_nsec = (ticks % _TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100;
return base;
} else if (base == TIME_MONOTONIC || base == TIME_MONOTONIC_RAW) {
if (frequency.QuadPart == 0) {
static once_flag once = ONCE_FLAG_INIT;
call_once(&once, c23_timespec_get_init);
}
if (frequency.QuadPart != 0) {
LARGE_INTEGER now;
LONGLONG sec;
LONGLONG nsec;
QueryPerformanceCounter(&now);
sec = now.QuadPart / frequency.QuadPart;
nsec = (now.QuadPart - sec * frequency.QuadPart)
* 1000000000UL / frequency.QuadPart;
ts->tv_sec = (time_t)sec;
ts->tv_nsec = (long)nsec;
return base;
}
/* Otherwise timespec_get with TIME_MONOTONIC or TIME_MONOTONIC_RAW failed */
return 0;
}
return 0;
#undef _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS
#undef _TIMESPEC_IMPL_TICKS_PER_SECONDS
}
#else
int c23_timespec_get(struct timespec *ts, int base)
{
if (!ts)
return 0;
switch (base)
{
case TIME_UTC:
if (clock_gettime(CLOCK_REALTIME, ts) == 0)
return base;
break;
#ifdef CLOCK_MONOTONIC
case TIME_MONOTONIC:
if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
return base;
break;
#endif
#ifdef CLOCK_PROCESS_CPUTIME_ID
case TIME_ACTIVE:
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts) == 0)
return base;
break;
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
case TIME_THREAD_ACTIVE:
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, ts) == 0)
return base;
break;
#endif
#ifdef CLOCK_MONOTONIC_RAW
case TIME_MONOTONIC_RAW:
if (clock_gettime(CLOCK_MONOTONIC_RAW, ts) == 0)
return base;
break;
#endif
default:
break;
}
return 0;
}
#endif
#endif /* !HAVE_TIMESPEC_GET */
+192
View File
@@ -0,0 +1,192 @@
/*
* C11 <threads.h> emulation library
*
* (C) Copyright yohhoy 2012.
* Copyright 2022 Yonggang Luo
* 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 C11_THREADS_H_INCLUDED_
#define C11_THREADS_H_INCLUDED_
#include "c11/time.h"
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#if defined(_WIN32) && !defined(HAVE_PTHREAD)
# include <io.h> /* close */
# include <process.h> /* _exit */
#elif defined(HAVE_PTHREAD)
# include <pthread.h>
# include <unistd.h> /* close, _exit */
#else
# error Not supported on this platform.
#endif
#if defined(HAVE_THRD_CREATE)
#include <threads.h>
#else
/*---------------------------- macros ---------------------------*/
#ifndef _Thread_local
# if defined(__cplusplus)
/* C++11 doesn't need `_Thread_local` keyword or macro */
# elif !defined(__STDC_NO_THREADS__)
/* threads are optional in C11, _Thread_local present in this condition */
# elif defined(_MSC_VER)
# define _Thread_local __declspec(thread)
# elif defined(__GNUC__)
# define _Thread_local __thread
# else
/* Leave _Thread_local undefined so that use of _Thread_local would not promote
* to a non-thread-local global variable
*/
# endif
#endif
#if !defined(__cplusplus)
/*
* C11 thread_local() macro
* C++11 and above already have thread_local keyword
*/
# ifndef thread_local
# define thread_local _Thread_local
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------- types ----------------------------*/
typedef void (*tss_dtor_t)(void *);
typedef int (*thrd_start_t)(void *);
#if defined(_WIN32) && !defined(HAVE_PTHREAD)
typedef struct
{
void *Ptr;
} cnd_t;
/* Define thrd_t as struct type intentionally for avoid use of thrd_t as pointer type */
typedef struct
{
void *handle;
} thrd_t;
typedef unsigned long tss_t;
typedef struct
{
void *DebugInfo;
long LockCount;
long RecursionCount;
void *OwningThread;
void *LockSemaphore;
uintptr_t SpinCount;
} mtx_t; /* Mock of CRITICAL_SECTION */
typedef struct
{
volatile uintptr_t status;
} once_flag;
# define ONCE_FLAG_INIT {0}
# define TSS_DTOR_ITERATIONS 1
#elif defined(HAVE_PTHREAD)
typedef pthread_cond_t cnd_t;
typedef pthread_t thrd_t;
typedef pthread_key_t tss_t;
typedef pthread_mutex_t mtx_t;
#ifndef __once_flag_defined
typedef pthread_once_t once_flag;
# define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
#endif
# ifdef PTHREAD_DESTRUCTOR_ITERATIONS
# define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
# else
# define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once.
# endif
#else
# error Not supported on this platform.
#endif
/*-------------------- enumeration constants --------------------*/
enum
{
mtx_plain = 0x1,
mtx_recursive = 0x2,
mtx_timed = 0x4,
};
enum
{
thrd_success = 0, // succeeded
thrd_timedout, // timed out
thrd_error, // failed
thrd_busy, // resource busy
thrd_nomem // out of memory
};
/*-------------------------- functions --------------------------*/
void call_once(once_flag *, void (*)(void));
int cnd_broadcast(cnd_t *);
void cnd_destroy(cnd_t *);
int cnd_init(cnd_t *);
int cnd_signal(cnd_t *);
int cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict __mtx,
const struct timespec *__restrict);
int cnd_wait(cnd_t *, mtx_t *__mtx);
void mtx_destroy(mtx_t *__mtx);
int mtx_init(mtx_t *__mtx, int);
int mtx_lock(mtx_t *__mtx);
int mtx_timedlock(mtx_t *__restrict __mtx,
const struct timespec *__restrict);
int mtx_trylock(mtx_t *__mtx);
int mtx_unlock(mtx_t *__mtx);
int thrd_create(thrd_t *, thrd_start_t, void *);
thrd_t thrd_current(void);
int thrd_detach(thrd_t);
int thrd_equal(thrd_t, thrd_t);
#if defined(__cplusplus)
[[ noreturn ]]
#else
_Noreturn
#endif
void thrd_exit(int);
int thrd_join(thrd_t, int *);
int thrd_sleep(const struct timespec *, struct timespec *);
void thrd_yield(void);
int tss_create(tss_t *, tss_dtor_t);
void tss_delete(tss_t);
void *tss_get(tss_t);
int tss_set(tss_t, void *);
#ifdef __cplusplus
}
#endif
#endif /* HAVE_THRD_CREATE */
#endif /* C11_THREADS_H_INCLUDED_ */
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright 2022 Yonggang Luo
* SPDX-License-Identifier: MIT
*
* C11 <time.h> emulation library
*/
#ifndef C11_TIME_H_INCLUDED_
#define C11_TIME_H_INCLUDED_
#include <time.h>
/*---------------------------- macros ---------------------------*/
/* Refer to https://htmlpreview.github.io/?https://icube-forge.unistra.fr/icps/c23-library/-/raw/main/README.html#time_monotonic-time_active-time_thread_active */
#if defined(TIME_UTC) && \
defined(TIME_MONOTONIC) && \
defined(TIME_ACTIVE) && \
defined(TIME_THREAD_ACTIVE) && \
defined(TIME_MONOTONIC_RAW)
/* all needed time base is implemented */
#else
#define _TIMESPEC_GET_NEED_IMPL
#endif
#ifdef _TIMESPEC_GET_NEED_IMPL
#undef TIME_UTC
#undef TIME_MONOTONIC
#undef TIME_ACTIVE
#undef TIME_THREAD_ACTIVE
#undef TIME_MONOTONIC_RAW
/* c11 */
#define TIME_UTC 1
/* c23 */
#define TIME_MONOTONIC 2
#define TIME_ACTIVE 3
#define TIME_THREAD_ACTIVE 4
#define TIME_MONOTONIC_RAW 5
#define timespec_get c23_timespec_get
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------- types ----------------------------*/
/*
* On MINGW `struct timespec` present but `timespec_get` may not present;
* On MSVC `struct timespec` and `timespec_get` present at the same time;
* So detecting `HAVE_STRUCT_TIMESPEC` in meson script dynamically.
*/
#ifndef HAVE_STRUCT_TIMESPEC
struct timespec
{
time_t tv_sec; // Seconds - >= 0
long tv_nsec; // Nanoseconds - [0, 999999999]
};
#endif
/*-------------------------- functions --------------------------*/
#if defined(_TIMESPEC_GET_NEED_IMPL)
#define _TIMESPEC_GET_NEED_DECL
#elif defined(__APPLE__) && defined(__cplusplus) && (__cplusplus < 201703L)
/* On macOS, the guard for declaration of timespec_get is by
* (defined(__cplusplus) && __cplusplus >= 201703L),
* fix the declaration for C++14 and lower here
*/
#define _TIMESPEC_GET_NEED_DECL
#endif
#ifdef _TIMESPEC_GET_NEED_DECL
/*-------------------- 7.25.7 Time functions --------------------*/
// 7.25.6.1
int
timespec_get(struct timespec *ts, int base);
#undef _TIMESPEC_GET_NEED_DECL
#endif
#ifdef __cplusplus
}
#endif
#endif /* C11_TIME_H_INCLUDED_ */