Skip to content

Instantly share code, notes, and snippets.

@usm-takl
Last active October 28, 2020 03:49
Show Gist options
  • Save usm-takl/c5f903638daf03bc6f6a01d2d555372d to your computer and use it in GitHub Desktop.
Save usm-takl/c5f903638daf03bc6f6a01d2d555372d to your computer and use it in GitHub Desktop.
//
// 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