Skip to content

Instantly share code, notes, and snippets.

@aprell
Created September 14, 2012 16:31
Show Gist options
  • Save aprell/3723066 to your computer and use it in GitHub Desktop.
Save aprell/3723066 to your computer and use it in GitHub Desktop.
Asynchronous function call / task support macros
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "async.h"
typedef struct {
void *fn; // pointer to piece of code
unsigned char data[128]; // data environment
} Task;
// Fixed-size pseudo task pool
#define MAXNTASKS 10
typedef struct {
int ntasks;
Task tasks[MAXNTASKS];
} Taskpool;
static Taskpool TP;
bool task_enqueue(Taskpool *tpool, Task *t)
{
if (tpool->ntasks == MAXNTASKS)
return false;
memcpy(&tpool->tasks[tpool->ntasks++], t, sizeof(*t));
return true;
}
bool task_dequeue(Taskpool *tpool, Task *t)
{
if (tpool->ntasks == 0)
return false;
memcpy(t, &tpool->tasks[--tpool->ntasks], sizeof(*t));
return true;
}
void task_run(Task *t)
{
(*(void (*)(void *))t->fn)(t->data);
}
void hello(const char *s)
{
printf("Hello %s\n", s);
}
int sum(int a, int b)
{
return a + b;
}
ASYNC_DECL(hello, const char *s, s);
FUTURE_DECL(int, sum, int a; int b, a, b);
int main(void)
{
ASYNC(hello, "Universe");
ASYNC(hello, "World");
ASYNC(hello, "Town");
Task t;
while (task_dequeue(&TP, &t))
task_run(&t);
int s = 0;
ASYNC(sum, 40, 30, &s);
ASYNC(sum, 30, 20, &s);
ASYNC(sum, 20, 10, &s);
while (task_dequeue(&TP, &t)) {
task_run(&t);
printf("Sum is %d\n", s);
}
return 0;
}
#ifndef ASYNC_H
#define ASYNC_H
/* Count variadic macro arguments (1-10 arguments, extend as needed)
*/
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
/* Pack args into data structure x
*/
#define PACK(x, args...) \
do { \
*(x) = (typeof(*(x))){ args }; \
} while (0)
/* Unpack data structure x (up to five members, extend as needed)
*/
#define UNPACK1(x, _1) \
typeof((x)->_1) _1 = (x)->_1
#define UNPACK2(x, _1, _2) \
typeof((x)->_1) _1 = (x)->_1; \
typeof((x)->_2) _2 = (x)->_2
#define UNPACK3(x, _1, _2, _3) \
typeof((x)->_1) _1 = (x)->_1; \
typeof((x)->_2) _2 = (x)->_2; \
typeof((x)->_3) _3 = (x)->_3
#define UNPACK4(x, _1, _2, _3, _4) \
typeof((x)->_1) _1 = (x)->_1; \
typeof((x)->_2) _2 = (x)->_2; \
typeof((x)->_3) _3 = (x)->_3; \
typeof((x)->_4) _4 = (x)->_4
#define UNPACK5(x, _1, _2, _3, _4, _5) \
typeof((x)->_1) _1 = (x)->_1; \
typeof((x)->_2) _2 = (x)->_2; \
typeof((x)->_3) _3 = (x)->_3; \
typeof((x)->_4) _4 = (x)->_4; \
typeof((x)->_5) _5 = (x)->_5
#define UNPACK_IMPL2(x, n, ...) UNPACK ## n(x, __VA_ARGS__)
#define UNPACK_IMPL(x, n, ...) UNPACK_IMPL2(x, n, __VA_ARGS__)
#define UNPACK(x, ...) UNPACK_IMPL(x, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
/* Allows for function fname to be called asynchronously
*
* Examples:
* ASYNC_DECL(do_sth, int a; int b, a, b);
* FUTURE_DECL(int, do_sth_else, int a; int b, a, b);
*/
#define ASYNC_DECL(fname, decls, args...) \
struct fname##_task_data { \
decls; \
}; \
void fname##_task_func(struct fname##_task_data *d) \
{ \
UNPACK(d, args); \
fname(args); \
}
/* Async functions with return values are basically futures
*/
#define FUTURE_DECL(ret, fname, decls, args...) \
struct fname##_task_data { \
decls; \
ret *__v; \
}; \
void fname##_task_func(struct fname##_task_data *d) \
{ \
UNPACK(d, args, __v); \
ret tmp = fname(args); \
*(__v) = tmp; \
}
/* Call f(args) asynchronously
*
* Requires underlying tasking implementation
*
* Examples:
* ASYNC(do_sth, a, b);
* ASYNC(do_sth_else, a, b, &c); // function with return value
*/
#define ASYNC(f, args...) \
do { \
Task __t = { .fn = f##_task_func }; \
struct f##_task_data *__d = (struct f##_task_data *)__t.data; \
PACK(__d, args); \
task_enqueue(&TP, &__t); \
} while (0)
#endif // ASYNC_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment