-
-
Save usm-takl/c5f903638daf03bc6f6a01d2d555372d to your computer and use it in GitHub Desktop.
This is for a post on Qiita https://qiita.com/takl/items/6ffe14db22974b1f74ce
This file contains hidden or 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
// | |
// This file consists of 3 parts. | |
// The first 2 parts are from yohhoy's C11 <threads.h> emulation library. | |
// The original files are from https://gist.github.com/yohhoy/2223710 | |
// I, takl, commented out 2 lines. | |
// These 2 parts are ditributed under the Boost Software License, Version 1.0. | |
// (See the header of these parts for detail) | |
// | |
// The last part is written by takl. | |
// This part is licensed under CC0. | |
// https://creativecommons.org/publicdomain/zero/1.0/deed.ja | |
// | |
// | |
// threads.h by yohhoy. | |
// | |
/* | |
* 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_ */ | |
// | |
// threads_posix.c by yohhoy | |
// | |
/* | |
* 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; | |
} | |
// | |
// The rest of this file is written by takl. | |
// This part is licensed under CC0. | |
// https://creativecommons.org/publicdomain/zero/1.0/deed.ja | |
// | |
#include<stdio.h> | |
#include<stdlib.h> | |
enum stack_command { | |
STACK_COMMAND_NONE, | |
STACK_COMMAND_PUSH, | |
STACK_COMMAND_POP, | |
}; | |
struct stack { | |
thrd_t thrd; | |
volatile enum stack_command command; | |
volatile int argument; | |
cnd_t cnd; | |
mtx_t mtx; | |
}; | |
void stack_not_empty(struct stack *stack, int cell) { | |
stack->command = STACK_COMMAND_NONE; | |
cnd_signal(&stack->cnd); | |
while (1) { | |
switch (stack->command) { | |
case STACK_COMMAND_NONE: | |
cnd_wait(&stack->cnd, &stack->mtx); | |
break; | |
case STACK_COMMAND_PUSH: | |
stack_not_empty(stack, stack->argument); | |
break; | |
case STACK_COMMAND_POP: | |
stack->argument = cell; | |
stack->command = STACK_COMMAND_NONE; | |
cnd_signal(&stack->cnd); | |
return; | |
} | |
} | |
} | |
void stack_empty(struct stack *stack) { | |
while (1) { | |
switch (stack->command) { | |
case STACK_COMMAND_NONE: | |
cnd_wait(&stack->cnd, &stack->mtx); | |
break; | |
case STACK_COMMAND_PUSH: | |
stack_not_empty(stack, stack->argument); | |
break; | |
case STACK_COMMAND_POP: | |
stack->argument = 0; | |
stack->command = STACK_COMMAND_NONE; | |
cnd_signal(&stack->cnd); | |
break; | |
} | |
} | |
} | |
int stack_start(void *stack_) { | |
struct stack *stack = stack_; | |
mtx_lock(&stack->mtx); | |
stack_empty(stack); | |
return 0; | |
} | |
void stack_init(struct stack *stack) { | |
stack->command = STACK_COMMAND_NONE; | |
cnd_init(&stack->cnd); | |
mtx_init(&stack->mtx, mtx_plain); | |
mtx_lock(&stack->mtx); | |
thrd_create(&stack->thrd, stack_start, stack); | |
} | |
void stack_push(struct stack *stack, int i) { | |
stack->command = STACK_COMMAND_PUSH; | |
stack->argument = i; | |
cnd_signal(&stack->cnd); | |
cnd_wait(&stack->cnd, &stack->mtx); | |
} | |
int stack_pop(struct stack *stack) { | |
stack->command = STACK_COMMAND_POP; | |
cnd_signal(&stack->cnd); | |
cnd_wait(&stack->cnd, &stack->mtx); | |
return stack->argument; | |
} | |
const char * const prog = PROG; | |
const char *back(const char *ip) { | |
int nest = 0; | |
while (1) { | |
if (ip == prog) { | |
printf("invalid ']'\n"); | |
exit(EXIT_FAILURE); | |
} | |
switch (*--ip) { | |
case '[': | |
if (nest == 0) return ip; | |
--nest; | |
break; | |
case ']': | |
++nest; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
const char *forth(const char *ip) { | |
int nest = 0; | |
while (1) { | |
switch (*ip++) { | |
case '\0': | |
printf("invalid '['\n"); | |
exit(EXIT_FAILURE); | |
case '[': | |
++nest; | |
break; | |
case ']': | |
if (nest == 0) return ip; | |
--nest; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
int main() { | |
struct stack ls, rs; | |
stack_init(&ls); | |
stack_init(&rs); | |
int cell = 0; | |
const char *ip = prog; | |
while (1) { | |
switch(*ip++) { | |
case '\0': | |
return 0; | |
case '+': | |
++cell; | |
break; | |
case '-': | |
--cell; | |
break; | |
case ';': | |
printf("[%d]", cell); | |
fflush(stdout); | |
break; | |
case '.': | |
printf("%c", cell); | |
fflush(stdout); | |
break; | |
case ',': | |
cell = getchar(); | |
break; | |
case '>': | |
stack_push(&ls, cell); | |
cell = stack_pop(&rs); | |
break; | |
case '<': | |
stack_push(&rs, cell); | |
cell = stack_pop(&ls); | |
break; | |
case '[': | |
if (cell == 0) ip = forth(ip); | |
break; | |
case ']': | |
if (cell != 0) ip = back(ip - 1) + 1; | |
break; | |
default: | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment