From 01f09f3c2f34ae960b091f6d4063750bccd3e777 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Thu, 23 Feb 2023 12:38:38 +0100 Subject: Renamed cco.h to coroutine.h --- include/stc/algo/cco.h | 134 ------------------------------------------- include/stc/algo/coroutine.h | 134 +++++++++++++++++++++++++++++++++++++++++++ misc/examples/cofib.c | 24 ++++---- misc/examples/coread.c | 8 +-- 4 files changed, 150 insertions(+), 150 deletions(-) delete mode 100644 include/stc/algo/cco.h create mode 100644 include/stc/algo/coroutine.h diff --git a/include/stc/algo/cco.h b/include/stc/algo/cco.h deleted file mode 100644 index c94382b1..00000000 --- a/include/stc/algo/cco.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * coroutine.h is copyright 1995, 2000 Simon Tatham. - * Modernized/improved 2023 Tyge Løvset. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -/* - * Coroutine mechanics, implemented on top of standard ANSI C. See - * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for - * a full discussion of the theory behind this. - * - * To use these macros to define a coroutine, you need to write a - * function that looks something like this: - * - * [Re-entrant version using an explicit context structure] - * - * int ascending(cco_handle* ctx) { - * cco_context(ctx, - * int idx; - * ); - * - * cco_routine(c, - * for (c->idx = 0; c->idx < 10; c->idx += 1) - * cco_yield(c->idx); - * - * cco_finish:; // add this to support cco_stop! - * ); - * return -1; - * } - * - * In the re-entrant version, you need to declare your persistent - * variables by the `cco_context' macro. This macro takes the - * cco_handle as first parameter. - * - * Note that the context variable is set back to zero when the - * coroutine terminates (by cco_stop, or by control reaching - * finish. This can make the re-entrant coroutines more useful - * than the static ones, because you can tell when they have - * finished. - * - * This mechanism could have been better implemented using GNU C - * and its ability to store pointers to labels, but sadly this is - * not part of the ANSI C standard and so the mechanism is done by - * case statements instead. That's why you can't put a cco_yield() - * inside a switch-statement. - * - * The re-entrant macros will malloc() the state structure on first - * call, and free() it when end is reached. If you want to - * abort in the middle, you can use, the caller should call the - * coroutine with `cco_stop(&ctx)' as parameter instead of &ctx alone. - * - * Ground rules: - * - never put `cco_yield' within an explicit `switch'. - * - never put two `cco_yield' statements on the same source line. - * - add `cco_finish:' label at the end of the coroutine, before - * any cleanup code. Required to support cco_stop(ctx) argument. - * - * The caller of a re-entrant coroutine must provide a context - * variable: - * - * void main(void) { - * cco_handle z = 0; - * for (;;) { - * int x = ascending(&z); - * if (!z) break; - * printf("got number %d\n", x); - * - * // stop if x == 5: - * if (x == 5) ascending(cco_stop(&z)); - * } - * } - */ - -#ifndef COROUTINE_H -#define COROUTINE_H - -#include -#include -/* - * `c_' macros for re-entrant coroutines. - */ -typedef struct { - int cco_line; -} *cco_handle; - -#define cco_context(handle, ...) \ - struct ccoContext { \ - int cco_line; \ - __VA_ARGS__ \ - } **_ccoparam = (struct ccoContext **)handle + 0*sizeof((*(handle))->cco_line) - -#define cco_routine(ctx, ...) \ - if (!*_ccoparam) { \ - *_ccoparam = (struct ccoContext *)malloc(sizeof **_ccoparam); \ - (*_ccoparam)->cco_line = 0; \ - } \ - struct ccoContext *ctx = *_ccoparam; \ - switch (ctx->cco_line) { \ - case 0: __VA_ARGS__ break; \ - default: assert(!"cco_finish: missing"); \ - } \ - free(ctx), *_ccoparam = NULL - -#define cco_yield(ret) \ - do { \ - (*_ccoparam)->cco_line = __LINE__; return ret; \ - case __LINE__:; \ - } while (0) - -#define cco_finish case -1 - -#define cco_stop(ctx) \ - ((*(ctx))->cco_line = -1, (ctx)) - -#endif /* COROUTINE_H */ diff --git a/include/stc/algo/coroutine.h b/include/stc/algo/coroutine.h new file mode 100644 index 00000000..cf7acc16 --- /dev/null +++ b/include/stc/algo/coroutine.h @@ -0,0 +1,134 @@ +/* + * coroutine.h is copyright 1995, 2000 Simon Tatham. + * Modernized/improved 2023 Tyge Løvset. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* + * Coroutine mechanics, implemented on top of standard ANSI C. See + * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for + * a full discussion of the theory behind this. + * + * To use these macros to define a coroutine, you need to write a + * function that looks something like this: + * + * + * int ascending(cco_handle* ctx) { + * cco_context(ctx, + * int idx; + * ); + * + * cco_routine(c, + * for (c->idx = 0; c->idx < 10; c->idx += 1) + * cco_yield(c->idx); + * + * cco_finish:; // add this to support cco_stop! + * ); + * + * return -1; + * } + * + * You declare your persistent variables with the `cco_context' + * macro. This macro takes the cco_handle as first parameter. + * + * Note that the context variable is set back to zero when the + * coroutine terminates (by cco_stop, or by control reaching + * finish. This can make the re-entrant coroutines more useful + * than the static ones, because you can tell when they have + * finished. + * + * This mechanism could have been better implemented using GNU C + * and its ability to store pointers to labels, but sadly this is + * not part of the ANSI C standard and so the mechanism is done by + * case statements instead. That's why you can't put a cco_yield() + * inside a switch-statement. + * + * The macros will malloc() the state structure on first call, and + * free() it when end is reached. If you want to abort in the middle, + * the caller must call the coroutine with `cco_stop(&ctx)' as + * parameter instead of &ctx. + * + * + * Ground rules: + * - never put `cco_yield' within an explicit `switch'. + * - never put two `cco_yield' statements on the same source line. + * - add `cco_finish:' label at the end of the coroutine, before + * any cleanup code. Required to support cco_stop(ctx) argument. + * - in order to stop before all items are consumed, call the + * coroutine with `cco_stop(&handle)' as argument at the end. + * + * The caller of a re-entrant coroutine must provide a context + * variable: + * + * void main(void) { + * cco_handle hnd = 0; + * for (;;) { + * int x = ascending(&hnd); + * if (x == 5) cco_stop(&hnd); + * if (!hnd) break; + * printf("got number %d\n", x); + * + * } + * } + */ +#ifndef STC_COROUTINE_INCLUDED +#define STC_COROUTINE_INCLUDED + +#include +#include +/* + * `c_' macros for re-entrant coroutines. + */ +typedef struct ccoHandle { + int cco_line; +} *cco_handle; + +#define cco_context(handle, ...) \ + struct ccoContext { \ + int cco_line; \ + __VA_ARGS__ \ + } **_ccoparam = (struct ccoContext **)handle + 0*sizeof((*(handle))->cco_line) + +#define cco_routine(ctx, ...) \ + if (!*_ccoparam) { \ + *_ccoparam = (struct ccoContext *)malloc(sizeof **_ccoparam); \ + (*_ccoparam)->cco_line = 0; \ + } \ + struct ccoContext *ctx = *_ccoparam; \ + switch (ctx->cco_line) { \ + case 0: __VA_ARGS__ break; \ + default: assert(!"missing cco_finish:"); \ + } \ + free(ctx), *_ccoparam = NULL + +#define cco_yield(value) \ + do { \ + (*_ccoparam)->cco_line = __LINE__; return value; \ + case __LINE__:; \ + } while (0) + +#define cco_finish case -1 + +static inline cco_handle* cco_stop(cco_handle* handle) + { (*handle)->cco_line = -1; return handle; } + +#endif // STC_COROUTINE_INCLUDED diff --git a/misc/examples/cofib.c b/misc/examples/cofib.c index 2363989a..ea37669a 100644 --- a/misc/examples/cofib.c +++ b/misc/examples/cofib.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -12,19 +12,19 @@ llong fibonacci_sequence(cco_handle* z, unsigned n) { cco_context(z, llong a, b, idx; ); - - cco_routine(u, - u->a = 0; - u->b = 1; - for (u->idx = 2; u->idx < n; u->idx++) { - llong sum = u->a + u->b; // NB! locals only lasts until a cco_yield! - u->a = u->b; - u->b = sum; - cco_yield (sum); + cco_routine(U, + U->a = 0; + U->b = 1; + for (U->idx = 0; U->idx < n; U->idx++) { + cco_yield (U->a); + llong sum = U->a + U->b; // NB! locals only lasts until next cco_yield! + U->a = U->b; + U->b = sum; } cco_finish: ); - return 0; + + return -1; } @@ -32,7 +32,7 @@ int main(void) { cco_handle z = 0; printf("Fibonacci numbers:\n"); for (;;) { - llong x = fibonacci_sequence(&z, 30); + llong x = fibonacci_sequence(&z, 14); if (!z) break; printf(" %lld", x); } diff --git a/misc/examples/coread.c b/misc/examples/coread.c index 55e4bf21..e65ab533 100644 --- a/misc/examples/coread.c +++ b/misc/examples/coread.c @@ -1,5 +1,5 @@ #include -#include +#include #include // Read file line by line using coroutines: @@ -10,7 +10,6 @@ cstr file_nextline(cco_handle* z, const char* name) FILE* fp; cstr line; ); - cco_routine(U, U->fp = fopen(name, "r"); U->line = cstr_NULL; @@ -23,6 +22,7 @@ cstr file_nextline(cco_handle* z, const char* name) cstr_drop(&U->line); fclose(U->fp); ); + return cstr_NULL; } @@ -34,8 +34,8 @@ int main(void) { c_with (cstr line = file_nextline(&z, __FILE__), z, cstr_drop(&line)) { printf("%3d %s\n", ++n, cstr_str(&line)); - // stop after 10 lines: - if (n == 10) file_nextline(cco_stop(&z), __FILE__); + // stop after 15 lines: + if (n == 15) file_nextline(cco_stop(&z), __FILE__); } } while (z); } -- cgit v1.2.3