diff --git a/README.md b/README.md index dcf7c8e..6936e0c 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,69 @@ Function|Description *async_call(func, state)*|Asynchronously call `func(state)` and return true if done executing (optional). You can also simply call `func(state)`directly which returns true/false. *async_init(state)*|Initialize async subroutine state *async_done(state)*|Returns true if async subroutine has completed execution, otherwise false +*async_run(cond)*|Runs your async function(s) from a non async function separated by `&` or `\|` to specify when all or one complete respectively # Examples +The following is a sample program where multiple coroutines print they are starting, sleep for a specified time, and +print they slept the specified time +```c +#include "async.h" +#include "async-time.h" + +struct print_after_state +{ + async_state; + struct async_sleep_state async_sleep_state; +}; + +static async print_after(struct print_after_state *state, unsigned int duration) +{ + async_begin(state); + + printf("Starting %d second counter...\n", duration); + async_yield; + printf("Yield from %d second counter demo'd!\n", duration); + async_init(&state->async_sleep_state); + await(async_sleep(&state->async_sleep_state, duration * 1000)); + printf("Slept: %d seconds\n", duration); + + async_end; +} + +int example_print_after(void) +{ + struct print_after_state a = {}, b = {}, c = {}; + + async_run( + print_after(&a, 3) & + print_after(&b, 5) & + print_after(&c, 1) + ); + + return 0; +} +``` + +Prints the following and runs a total of 5 seconds: + +``` +Starting 3 second counter... +Starting 5 second counter... +Starting 1 second counter... +Yield from 3 second counter demo'd! +Yield from 5 second counter demo'd! +Yield from 1 second counter demo'd! +Slept: 1 seconds +Slept: 3 seconds +Slept: 5 seconds +``` + I ported the examples found in the protothreads distribution to async.h. Here is the async.h equivalent of the protothreads sample on the home page: -```C +```c #include "async.h" +#include "async-time.h" struct async pt; struct timer timer; @@ -40,7 +96,7 @@ async example(struct async *pt) { while(1) { if(initiate_io()) { - timer_start(&timer); + timer_set(&timer); await(io_completed() || timer_expired(&timer)); read_data(); } @@ -54,12 +110,13 @@ to accept the async structure/local continuation as an argument. Here is the same example as above, but where the timer is lifted to a local parameter: -```C +```c #include "async.h" +#include "async-time.h" typedef struct { async_state; // declare the asynchronous state - timer timer; // declare local state + struct timer timer; // declare local state } example_state; example_state pt; @@ -68,7 +125,7 @@ async example(example_state *pt) { while(1) { if(initiate_io()) { - timer_start(&pt->timer); + timer_set(&pt->timer); await(io_completed() || timer_expired(&pt->timer)); read_data(); } diff --git a/async/Makefile b/async/Makefile index c664808..290c6b2 100644 --- a/async/Makefile +++ b/async/Makefile @@ -2,7 +2,7 @@ CC = gcc CCFlags = -Wall BUILD_DIR = build -SRC = example-buffer.c example-codelock.c example-small.c main.c +SRC = async-time.c example-buffer.c example-codelock.c example-print-after.c example-small.c main.c OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(SRC)) all : $(OBJ) diff --git a/async/async-time.c b/async/async-time.c new file mode 100644 index 0000000..7d64d12 --- /dev/null +++ b/async/async-time.c @@ -0,0 +1,49 @@ +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "async.h" +#include "async-time.h" + +#ifdef _WIN32 + +static int clock_time(void) +{ + return (int)GetTickCount(); +} + +#else /* _WIN32 */ + +static int clock_time(void) +{ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +#endif /* _WIN32 */ + +int timer_expired(struct timer *t) +{ + return (int)(clock_time() - t->start) >= (int)t->interval; +} + +void timer_set(struct timer *t, unsigned int interval) +{ + t->interval = interval; + t->start = clock_time(); +} + +async async_sleep(struct async_sleep_state *state, unsigned int usecs) +{ + async_begin(state); + + timer_set(&state->timer, usecs); + await(timer_expired(&state->timer)); + + async_end; +} diff --git a/async/async-time.h b/async/async-time.h new file mode 100644 index 0000000..02accec --- /dev/null +++ b/async/async-time.h @@ -0,0 +1,42 @@ +#include "async.h" + +/** + * Timer structure for tracking when a timer has expired + */ +struct timer +{ + unsigned int start; + unsigned int interval; +}; + +/** + * State structure for calls to the async_sleep function + */ +struct async_sleep_state +{ + async_state; + struct timer timer; +}; + +/** + * Initialize a timer with a specified time duration + * + * @param t timer struct to initialize + * @param usecs number of milliseconds the timer is good for + */ +void timer_set(struct timer *t, unsigned int usecs); + +/** + * Determine if the supplied timer is expired + * + * @param t timer struct to examine for expiration + */ +int timer_expired(struct timer *t); + +/** + * Sleep asynchronously for a specified duration + * + * @param state async_sleep_state structure to hold state of operation + * @param usecs number of milliseconds to sleep for + */ +async async_sleep(struct async_sleep_state *state, unsigned int usecs); \ No newline at end of file diff --git a/async/async.h b/async/async.h index d22166c..7a64f1f 100644 --- a/async/async.h +++ b/async/async.h @@ -34,6 +34,7 @@ * Pulled from: https://github.com/naasking/async.h * * Author: Sandro Magi + * Contributor: Paul Sorensen */ /** @@ -135,4 +136,10 @@ struct async { async_state; }; */ #define async_call(f, state) (async_done(state) || (f)(state)) +/** + * Wait until the condition succeeds from a non-async function + * @param cond The condition that must be satisfied before execution can proceed + */ +#define async_run(cond) while(!(cond)) + #endif \ No newline at end of file diff --git a/async/example-buffer.c b/async/example-buffer.c index a55778d..2fe2fb6 100644 --- a/async/example-buffer.c +++ b/async/example-buffer.c @@ -146,19 +146,9 @@ example_buffer(void) async_init(&driver_pt); - while (!driver_thread(&driver_pt)) { - - /* - * When running this example on a multitasking system, we must - * give other processes a chance to run too and therefore we call - * usleep() resp. Sleep() here. On a dedicated embedded system, - * we usually do not need to do this. - */ -#ifdef _WIN32 - Sleep(0); -#else - usleep(10); -#endif - } + async_run( + driver_thread(&driver_pt) + ); + return 0; } diff --git a/async/example-codelock.c b/async/example-codelock.c index aeac93b..4c7c656 100644 --- a/async/example-codelock.c +++ b/async/example-codelock.c @@ -61,16 +61,8 @@ #include #include "async.h" +#include "async-time.h" -/*---------------------------------------------------------------------------*/ -/* - * The following definitions are just for the simple timer library - * used in this example. The actual implementation of the functions - * can be found at the end of this file. - */ -struct timer { int start, interval; }; -static int timer_expired(struct timer *t); -static void timer_set(struct timer *t, int usecs); /*---------------------------------------------------------------------------*/ /* * This example uses two timers: one for the code lock async and @@ -367,48 +359,11 @@ example_codelock(void) /* * Schedule the two asyncs until the codelock_thread() exits. */ - while(!codelock_thread(&codelock_pt)) { - input_thread(&input_pt); - - /* - * When running this example on a multitasking system, we must - * give other processes a chance to run too and therefore we call - * usleep() resp. Sleep() here. On a dedicated embedded system, - * we usually do not need to do this. - */ -#ifdef _WIN32 - Sleep(0); -#else - usleep(10); -#endif - } + async_run( + codelock_thread(&codelock_pt) & + input_thread(&input_pt) + ); return 0; } -/*---------------------------------------------------------------------------*/ -/* - * Finally, the implementation of the simple timer library follows. - */ -#ifdef _WIN32 - -static int clock_time(void) -{ return (int)GetTickCount(); } - -#else /* _WIN32 */ - -static int clock_time(void) -{ - struct timeval tv; - struct timezone tz; - gettimeofday(&tv, &tz); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; -} - -#endif /* _WIN32 */ -static int timer_expired(struct timer *t) -{ return (int)(clock_time() - t->start) >= (int)t->interval; } - -static void timer_set(struct timer *t, int interval) -{ t->interval = interval; t->start = clock_time(); } -/*---------------------------------------------------------------------------*/ diff --git a/async/example-print-after.c b/async/example-print-after.c new file mode 100644 index 0000000..6071d89 --- /dev/null +++ b/async/example-print-after.c @@ -0,0 +1,89 @@ +/** + * This is a another small example that shows how to use parts of async.h. + * The program consists of an async subroutine which does the following: + * + * 1. Prints that is starting a coroutine with a provided delay + * 2. Demonstrates yielding from a subroutine + * 3. Sleeps for a specified amount of time + * 4. Prints the duration it slept before completion + * + * This program also shows the use of the `async_run` macro for running + * async coroutines from a regular function + */ +#include +#include +#include + +#include "async.h" +#include "async-time.h" + +/** + * This structure is required because there will be an internal call to another + * async function (async_sleep) which contains its own state. Rather than having + * each state live in the top-level function call, this struct contains will + * save the state uniquely for each call. + */ +struct print_after_state +{ + async_state; + struct async_sleep_state async_sleep_state; +}; + +/** + * print_after takes a duration of time to sleep + * + * This demonstrates how async_yield works and also how multiple subroutines + * seemingly operate simultaneously + */ +static async print_after(struct print_after_state *state, unsigned int duration) +{ + /* Declare the beginning of the async subroutine */ + async_begin(state); + + /* This print line will show in the order in which the coroutines are called + However, each of coroutines will print this line before "Yield from..." + line is printed. This demonstrates how the async_yield operates by yielding + control to the next coroutine. */ + printf("Starting %d second counter...\n", duration); + async_yield; + printf("Yield from %d second counter demo'd!\n", duration); + + /* This will initialize the async_sleep_state to 0 */ + async_init(&state->async_sleep_state); + /* Asynchronously sleep for the specified duration */ + await(async_sleep(&state->async_sleep_state, duration * 1000)); + + /* This coroutine demonstration has slept for the provided duration */ + printf("Slept: %d seconds\n", duration); + + /* Declare the end of the async subroutine */ + async_end; +} + +/** + * This is the main function for this example. + * + * This demo will be using 3 coroutines and each require their own state. + * Each print_after call will print that they are starting in the order in which + * they are called, yield control, then sleep the specified duration before + * printing they have completed + */ +int example_print_after(void) +{ + /* Create 3 states. Memsetting each to zero like this effectively initialize + each state at once */ + struct print_after_state a = {}, b = {}, c = {}; + + /* Because we are not in an async portion of code, we cannot call 'await'. + In order to gather and call our coroutines, we will use 'async_run' to run + each. The & operator is used when you each call to run until completion, + while the | operator is used when only want each call to run until the first + completion occurs */ + async_run( + print_after(&a, 3) & + print_after(&b, 5) & + print_after(&c, 1) + ); + + return 0; +} \ No newline at end of file diff --git a/async/example-small.c b/async/example-small.c index 2d3cff6..c161a62 100644 --- a/async/example-small.c +++ b/async/example-small.c @@ -1,7 +1,8 @@ /** * This is a very small example that shows how to use * async.h. The program consists of two async subroutines that wait - * for each other to toggle a variable. + * for each other to toggle a variable. This continues while a third async + * subroutine counts down from a provided starting integer. */ /* We must always include async.h in our asyncs code. */ @@ -10,7 +11,7 @@ #include /* For printf(). */ /* Two flags that the two async functions use. */ -static int async1_flag, async2_flag; +static int async1_flag, async2_flag, counter; /** * The first async function. A async function must always @@ -71,27 +72,47 @@ async2(struct async *pt) async_end; } +/** + * An async function to decrement the global countdown + */ +static async +countdown(struct async *pt) +{ + async_begin(pt); + + /* Yield control to other functions while the counter is above 0 */ + while (counter-- >= 0) { + async_yield; + } + + async_end; +} + /** * Finally, we have the main loop. Here is where the asyncs are * initialized and scheduled. First, however, we define the - * async state variables pt1 and pt2, which hold the state of - * the two asyncs. + * async state variables pt1, pt2, and count, which hold the state of + * the 3 asyncs. */ -static struct async pt1, pt2; +static struct async pt1, pt2, count; void example_small(int i) { /* Initialize the async state variables with async_init(). */ async_init(&pt1); async_init(&pt2); + async_init(&count); + + counter = i; /* - * Then we schedule the two asyncs by repeatedly calling their - * async functions and passing a pointer to the async + * Then we schedule the three asyncs by repeatedly calling their async + * functions via the `async_run` macro and passing a pointer to the async * state variables as arguments. */ - while (--i >= 0) { - async1(&pt1); - async2(&pt2); - } + async_run( + countdown(&count) | + async1(&pt1) | + async2(&pt2) + ); } diff --git a/async/examples.h b/async/examples.h index 171b5b4..78e2537 100644 --- a/async/examples.h +++ b/async/examples.h @@ -4,5 +4,6 @@ extern void example_small(int); extern int example_buffer(void); extern int example_codelock(void); +extern int example_print_after(void); #endif diff --git a/async/main.c b/async/main.c index 8c1c80b..2967683 100644 --- a/async/main.c +++ b/async/main.c @@ -5,5 +5,6 @@ int main(void) { example_small(200); example_buffer(); example_codelock(); + example_print_after(); return 0; } \ No newline at end of file