Last active
March 2, 2022 17:07
-
-
Save HunterKohler/802bc45b97ccdbf4b8196c5338290ea0 to your computer and use it in GitHub Desktop.
Start of a super-simple UNIX ISO-C11 threads header.
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
/* | |
* Copyright (C) 2021-2022 John Hunter Kohler <jhunterkohler@gmail.com> | |
*/ | |
#include <stdlib.h> | |
#include <stdatomic.h> | |
#include <errno.h> | |
#include "threads.h" | |
union thrd_routine_info { | |
int ret; | |
struct { | |
thrd_start_t func; | |
void *arg; | |
}; | |
}; | |
int module_init_err; | |
pthread_mutexattr_t mtx_attr_plain; | |
pthread_mutexattr_t mtx_attr_timed; | |
pthread_mutexattr_t mtx_attr_plain_recursive; | |
pthread_mutexattr_t mtx_attr_timed_recursive; | |
thread_local union thrd_routine_info *routine_info; | |
static void module_init_routine() | |
{ | |
if ((module_init_err = pthread_mutexattr_init(&mtx_attr_plain)) || | |
(module_init_err = pthread_mutexattr_init(&mtx_attr_timed)) || | |
(module_init_err = pthread_mutexattr_init(&mtx_attr_plain_recursive)) || | |
(module_init_err = pthread_mutexattr_init(&mtx_attr_timed_recursive)) || | |
(module_init_err = pthread_mutexattr_settype(&mtx_attr_plain, | |
PTHREAD_MUTEX_NORMAL)) || | |
(module_init_err = pthread_mutexattr_settype(&mtx_attr_timed, | |
PTHREAD_MUTEX_NORMAL)) || | |
(module_init_err = pthread_mutexattr_settype( | |
&mtx_attr_plain_recursive, PTHREAD_MUTEX_RECURSIVE)) || | |
(module_init_err = pthread_mutexattr_settype( | |
&mtx_attr_timed_recursive, PTHREAD_MUTEX_RECURSIVE))) { | |
pthread_mutexattr_destroy(&mtx_attr_plain); | |
pthread_mutexattr_destroy(&mtx_attr_timed); | |
pthread_mutexattr_destroy(&mtx_attr_plain_recursive); | |
pthread_mutexattr_destroy(&mtx_attr_timed_recursive); | |
} | |
} | |
static int module_init() | |
{ | |
static pthread_once_t once = PTHREAD_ONCE_INIT; | |
pthread_once(&once, module_init_routine); | |
return module_init_err; | |
} | |
/* | |
* Get ISO thread error (or success) value from POSIX `errno` codes. | |
*/ | |
static int thrd_return_code(int n) | |
{ | |
switch (n) { | |
case 0: | |
return thrd_success; | |
case ENOMEM: | |
return thrd_nomem; | |
case EBUSY: | |
return thrd_busy; | |
default: | |
return thrd_error; | |
} | |
} | |
static void *thrd_routine(void *thrd_arg) | |
{ | |
routine_info = thrd_arg; | |
routine_info->ret = routine_info->func(routine_info->arg); | |
pthread_exit(routine_info); | |
} | |
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) | |
{ | |
union thrd_routine_info *info = malloc(sizeof(*info)); | |
if (!info) | |
return thrd_nomem; | |
info->func = func; | |
info->arg = arg; | |
int err = pthread_create(&thr->native_handle, NULL, thrd_routine, info); | |
if (err) | |
free(info); | |
return thrd_return_code(err); | |
} | |
int thrd_equal(thrd_t lhs, thrd_t rhs) | |
{ | |
return pthread_equal(lhs.native_handle, rhs.native_handle); | |
} | |
thrd_t thrd_current(void) | |
{ | |
return (thrd_t){ pthread_self() }; | |
} | |
// int thrd_sleep(const struct timespec *duration, struct timespec *remaining); | |
void thrd_yield(void) | |
{ | |
sched_yield(); | |
} | |
_Noreturn void thrd_exit(int res) | |
{ | |
routine_info->ret = res; | |
pthread_exit(routine_info); | |
} | |
int thrd_detach(thrd_t thr) | |
{ | |
return thrd_return_code(pthread_detach(thr.native_handle)); | |
} | |
int thrd_join(thrd_t thr, int *res) | |
{ | |
union thrd_routine_info *info; | |
int err = pthread_join(thr.native_handle, (void **)&info); | |
*res = info->ret; | |
free(info); | |
return thrd_return_code(err); | |
} | |
int mtx_init(mtx_t *mutex, int type) | |
{ | |
int err; | |
pthread_mutexattr_t *attr; | |
if ((err = module_init())) | |
return thrd_return_code(err); | |
switch (type) { | |
case mtx_plain: | |
attr = &mtx_attr_plain; | |
break; | |
case mtx_timed: | |
attr = &mtx_attr_timed; | |
break; | |
case mtx_plain | mtx_recursive: | |
attr = &mtx_attr_plain_recursive; | |
break; | |
case mtx_timed | mtx_recursive: | |
attr = &mtx_attr_timed_recursive; | |
break; | |
default: | |
return thrd_error; | |
} | |
return thrd_return_code(pthread_mutex_init(&mutex->native_handle, attr)); | |
} | |
int mtx_lock(mtx_t *mutex) | |
{ | |
return thrd_return_code(pthread_mutex_lock(&mutex->native_handle)); | |
} | |
// int mtx_timedlock(mtx_t *restrict mutex, | |
// const struct timespec *restrict time_point); | |
int mtx_trylock(mtx_t *mutex) | |
{ | |
return thrd_return_code(pthread_mutex_trylock(&mutex->native_handle)); | |
} | |
int mtx_unlock(mtx_t *mutex) | |
{ | |
return thrd_return_code(pthread_mutex_unlock(&mutex->native_handle)); | |
} | |
void mtx_destroy(mtx_t *mutex) | |
{ | |
pthread_mutex_destroy(&mutex->native_handle); | |
} | |
void call_once(once_flag *flag, void (*func)(void)) | |
{ | |
pthread_once(&flag->native_handle, func); | |
} | |
int cnd_init(cnd_t *cond) | |
{ | |
return thrd_return_code(pthread_cond_init(&cond->native_handle, NULL)); | |
} | |
int cnd_signal(cnd_t *cond) | |
{ | |
return thrd_return_code(pthread_cond_signal(&cond->native_handle)); | |
} | |
int cnd_broadcast(cnd_t *cond) | |
{ | |
return thrd_return_code(pthread_cond_broadcast(&cond->native_handle)); | |
} | |
int cnd_wait(cnd_t *cond, mtx_t *mutex) | |
{ | |
return thrd_return_code( | |
pthread_cond_wait(&cond->native_handle, &mutex->native_handle)); | |
} | |
// int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mutex, | |
// const struct timespec *restrict time_point); | |
void cnd_destroy(cnd_t *cond) | |
{ | |
pthread_cond_destroy(&cond->native_handle); | |
} | |
int tss_create(tss_t *tss_key, tss_dtor_t destructor) | |
{ | |
return thrd_return_code( | |
pthread_key_create(&tss_key->native_handle, destructor)); | |
} | |
void *tss_get(tss_t tss_key) | |
{ | |
return pthread_getspecific(tss_key.native_handle); | |
} | |
int tss_set(tss_t tss_id, void *val) | |
{ | |
return thrd_return_code(pthread_setspecific(tss_id.native_handle, val)); | |
} | |
void tss_delete(tss_t tss_id) | |
{ | |
pthread_key_delete(tss_id.native_handle); | |
} |
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
/* | |
* Copyright (C) 2021-2022 John Hunter Kohler <jhunterkohler@gmail.com> | |
*/ | |
#ifndef ISO_THREADS_THREADS_H_ | |
#define ISO_THREADS_THREADS_H_ | |
#include <time.h> | |
#include <pthread.h> | |
#include "threads.h" | |
#define thread_local _Thread_local | |
#define ONCE_FLAG_INIT ({ (pthread_once_t)PTHREAD_ONCE_INIT }) | |
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS | |
typedef int (*thrd_start_t)(void *); | |
typedef void (*tss_dtor_t)(void *); | |
typedef struct { | |
pthread_t native_handle; | |
} thrd_t; | |
typedef struct { | |
pthread_mutex_t native_handle; | |
} mtx_t; | |
typedef struct { | |
pthread_cond_t native_handle; | |
} cnd_t; | |
typedef struct { | |
pthread_key_t native_handle; | |
} tss_t; | |
typedef struct { | |
pthread_once_t native_handle; | |
} once_flag; | |
enum { | |
thrd_success, | |
thrd_nomem, | |
thrd_timedout, | |
thrd_busy, | |
thrd_error, | |
}; | |
enum { | |
mtx_plain = 1 << 0, | |
mtx_recursive = 1 << 1, | |
mtx_timed = 1 << 2, | |
}; | |
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); | |
int thrd_equal(thrd_t lhs, thrd_t rhs); | |
thrd_t thrd_current(void); | |
int thrd_sleep(const struct timespec *duration, struct timespec *remaining); | |
void thrd_yield(void); | |
_Noreturn void thrd_exit(int res); | |
int thrd_detach(thrd_t thr); | |
int thrd_join(thrd_t thr, int *res); | |
int mtx_init(mtx_t *mutex, int type); | |
int mtx_lock(mtx_t *mutex); | |
int mtx_timedlock(mtx_t *restrict mutex, | |
const struct timespec *restrict time_point); | |
int mtx_trylock(mtx_t *mutex); | |
int mtx_unlock(mtx_t *mutex); | |
void mtx_destroy(mtx_t *mutex); | |
void call_once(once_flag *flag, void (*func)(void)); | |
int cnd_init(cnd_t *cond); | |
int cnd_signal(cnd_t *cond); | |
int cnd_broadcast(cnd_t *cond); | |
int cnd_wait(cnd_t *cond, mtx_t *mutex); | |
int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mutex, | |
const struct timespec *restrict time_point); | |
void cnd_destroy(cnd_t *cond); | |
int tss_create(tss_t *tss_key, tss_dtor_t destructor); | |
void *tss_get(tss_t tss_key); | |
int tss_set(tss_t tss_id, void *val); | |
void tss_delete(tss_t tss_id); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment