///usr/bin/true; gcc -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 main.c -o main && ./main; exit $? #include #include #include #include #include #define RETURNWERR(eval, rval) do { \ errno = (eval); \ return (rval); \ } while (0) #define ERROR(exitval, errval, msg, __VA_ARGS__) do { \ error((exitval), (errval), (msg)__VA_ARGS__); \ abort(); \ } while (0) void * xcalloc(size_t nmemb, size_t size) { void *mem = calloc(nmemb, size); if(!mem) ERROR(-1, errno, " failed to allocate memory ", ); return mem; } typedef int (*gcallback)(void*); typedef void (*fcallback)(void*); typedef struct gdata { void *data; fcallback fcb; } gdata; gdata * gdata_init(void *data, fcallback fcb) { gdata *gd = xcalloc(1, sizeof(*gd)); gd->data = data; gd->fcb = fcb; return gd; } void gdata_free(void *gd) { if(!gd) return; gdata *real = (gdata*)gd; if(real->fcb != NULL) real->fcb(real->data); free(real); return; } void * gdata_getdata(gdata * const gd) { if(!gd) RETURNWERR(EINVAL, NULL); return gd->data; } void * gdata_destruct(gdata *gd) { if(!gd) RETURNWERR(EINVAL, NULL); void *data = gdata_getdata(gd); free(gd); return data; } typedef struct stack { gdata **arr; int size; int used; } stack; stack * stack_init(int size) { if(size < 1) RETURNWERR(EINVAL, NULL); stack *st = xcalloc(1, sizeof(*st)); st->size = size; st->used = 0; st->arr = xcalloc(st->size, sizeof(gdata*)); return st; } void stack_free(void *st) { if(!st) return; stack *real = (stack*)st; for(int i = 0; i < real->used; i++) gdata_free(real->arr[i]); free(real->arr); free(real); return; } int stack_push(stack *st, gdata *gd) { if(!st || !gd) RETURNWERR(EINVAL, -1); if(!(st->used < st->size)) RETURNWERR(ENOMEM, 0); st->arr[st->used++] = gd; return st->used; } gdata * stack_pop(stack *st) { if(!st) RETURNWERR(EINVAL, NULL); if(st->used <= 0) RETURNWERR(ENODATA, 0); return st->arr[--st->used]; } int stack_pushd(stack *st, void *data, fcallback fcb) { if(!st) RETURNWERR(EINVAL, -1); gdata *gd = gdata_init(data, fcb); int retval = stack_push(st, gd); if(retval <= 0) gdata_free(gd); return retval; } void * stack_popd(stack *st) { if(!st) RETURNWERR(EINVAL, NULL); gdata *gd = stack_pop(st); if(!gd) return NULL; return gdata_destruct(gd); } typedef struct cstack { stack *st; mtx_t mutex; cnd_t cond; unsigned char canceled; } cstack; cstack * cstack_init(int size) { if(size < 1) RETURNWERR(EINVAL, NULL); cstack *cst = xcalloc(1, sizeof(*cst)); cst->st = stack_init(size); cst->canceled = 0; mtx_init(&cst->mutex, mtx_plain); cnd_init(&cst->cond); return cst; } int cstack_cancel(cstack * const cst) { if(!cst) RETURNWERR(EINVAL, -1); mtx_lock(&cst->mutex); cst->canceled = 1; mtx_unlock(&cst->mutex); cnd_broadcast(&cst->cond); return 0; } void cstack_free(void *cst) { if(!cst) return; cstack *real = (cstack*)cst; cstack_cancel(real); // Ok I don't think there's a good way to wait for all threads to die without registering the threads return; }