Created
September 14, 2012 16:31
-
-
Save aprell/3723066 to your computer and use it in GitHub Desktop.
Asynchronous function call / task support macros
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
#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; | |
} |
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
#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