From 2d67f4040f6eecd41f1b864b43c62823ed75aff0 Mon Sep 17 00:00:00 2001 From: tylov Date: Fri, 21 Jul 2023 00:37:28 +0200 Subject: Renamed badly abbreviated names in crand.h. Moved coroutine.h from algo subfolder to stc. Updated coroutine.h and docs. --- include/stc/coroutine.h | 273 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 include/stc/coroutine.h (limited to 'include/stc/coroutine.h') diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h new file mode 100644 index 00000000..f89d20af --- /dev/null +++ b/include/stc/coroutine.h @@ -0,0 +1,273 @@ +/* MIT License + * + * Copyright (c) 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 THE + * AUTHORS OR COPYRIGHT HOLDERS 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. + */ +#ifndef STC_COROUTINE_INCLUDED +#define STC_COROUTINE_INCLUDED +/* +#include +#include + +struct iterpair { + int max_x, max_y; + int x, y; + int cco_state; // required member +}; + +int iterpair(struct iterpair* I) { + cco_routine(I) { + for (I->x = 0; I->x < I->max_x; I->x++) + for (I->y = 0; I->y < I->max_y; I->y++) + cco_yield(); + + cco_cleanup: // required if there is cleanup code + puts("final"); + } + return 0; // CCO_DONE +} + +int main(void) { + struct iterpair it = {.max_x=3, .max_y=3}; + int n = 0; + while (iterpair(&it)) + { + printf("%d %d\n", it.x, it.y); + // example of early stop: + if (++n == 7) cco_stop(&it); // signal to stop/finalize in next + } + return 0; +} +*/ +#include "ccommon.h" + +enum { + CCO_STATE_CLEANUP = -1, + CCO_STATE_DONE = -2, +}; +typedef enum { + CCO_DONE = 0, + CCO_AWAIT = 1<<0, + CCO_YIELD = 1<<1, +} cco_result; + +#define cco_initial(co) ((co)->cco_state == 0) +#define cco_suspended(co) ((co)->cco_state > 0) +#define cco_done(co) ((co)->cco_state == CCO_STATE_DONE) + +#define cco_routine(co) \ + for (int* _state = &(co)->cco_state; *_state != CCO_STATE_DONE; *_state = CCO_STATE_DONE) \ + _resume: switch (*_state) case 0: // thanks, @liigo! + +#define cco_yield() cco_yield_v(CCO_YIELD) +#define cco_yield_v(ret) \ + do { \ + *_state = __LINE__; return ret; goto _resume; \ + case __LINE__:; \ + } while (0) + +#define cco_await(promise) cco_await_v_2(promise, CCO_AWAIT) +#define cco_await_v(...) c_MACRO_OVERLOAD(cco_await_v, __VA_ARGS__) +#define cco_await_v_1(promise) cco_await_v_2(promise, ) +#define cco_await_v_2(promise, ret) \ + do { \ + *_state = __LINE__; \ + case __LINE__: if (!(promise)) {return ret; goto _resume;} \ + } while (0) + +/* cco_await_on(): assumes coroutine returns a cco_result value (int) */ +#define cco_await_on(corocall) \ + do { \ + *_state = __LINE__; \ + case __LINE__: { int _r = corocall; if (_r != CCO_DONE) {return _r; goto _resume;} } \ + } while (0) + +/* cco_block_on(): assumes coroutine returns a cco_result value (int) */ +#define cco_block_on(...) c_MACRO_OVERLOAD(cco_block_on, __VA_ARGS__) +#define cco_block_on_1(corocall) while ((corocall) != CCO_DONE) +#define cco_block_on_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) + +#define cco_cleanup \ + *_state = CCO_STATE_CLEANUP; case CCO_STATE_CLEANUP + +#define cco_return \ + do { \ + *_state = *_state >= 0 ? CCO_STATE_CLEANUP : CCO_STATE_DONE; \ + goto _resume; \ + } while (0) + +#define cco_yield_final() cco_yield_final_v(CCO_YIELD) +#define cco_yield_final_v(value) \ + do { \ + *_state = *_state >= 0 ? CCO_STATE_CLEANUP : CCO_STATE_DONE; \ + return value; \ + } while (0) + +#define cco_stop(co) \ + do { \ + int* _s = &(co)->cco_state; \ + if (*_s > 0) *_s = CCO_STATE_CLEANUP; \ + else if (*_s == 0) *_s = CCO_STATE_DONE; \ + } while (0) + +#define cco_reset(co) \ + (void)((co)->cco_state = 0) + +/* + * Tasks (optional) + */ + +struct cco_runtime; + +#define cco_task_struct(Name, ...) \ + struct Name { \ + int (*cco_func)(struct Name*, struct cco_runtime*); \ + int cco_state, cco_expect; \ + __VA_ARGS__ \ + } + +typedef cco_task_struct(cco_task, /**/) cco_task; + +typedef struct cco_runtime { + int result, top; cco_task* stack[]; +} cco_runtime; + +#define cco_cast_task(task) \ + ((cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state))) + +#define cco_resume(task, rt) \ + (task)->cco_func(task, rt) + +#define cco_task_await(...) c_MACRO_OVERLOAD(cco_task_await, __VA_ARGS__) +#define cco_task_await_2(task, rt) cco_task_await_3(task, rt, CCO_DONE) +#define cco_task_await_3(task, rt, resultbits) \ + do { \ + cco_runtime* _rt = rt; \ + (_rt->stack[++_rt->top] = cco_cast_task(task))->cco_expect = (resultbits); \ + cco_yield_v(CCO_AWAIT); \ + } while (0) + +#define cco_task_block_on(...) c_MACRO_OVERLOAD(cco_task_block_on, __VA_ARGS__) +#define cco_task_block_on_1(task) cco_task_block_on_3(task, _rt, 16) +#define cco_task_block_on_3(task, rt, STACKDEPTH) \ + for (struct { int result, top; cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \ + (((rt.result = cco_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) + +/* + * Semaphore + */ + +typedef struct { intptr_t count; } cco_sem; + +#define cco_sem_await(sem) cco_sem_await_v_2(sem, CCO_AWAIT) +#define cco_sem_await_v(...) c_MACRO_OVERLOAD(cco_sem_await_v, __VA_ARGS__) +#define cco_sem_await_v_1(sem) cco_sem_await_v_2(sem, ) +#define cco_sem_await_v_2(sem, ret) \ + do { \ + cco_await_v_2((sem)->count > 0, ret); \ + --(sem)->count; \ + } while (0) + +#define cco_sem_release(sem) ++(sem)->count +#define cco_sem_from(value) ((cco_sem){value}) +#define cco_sem_set(sem, value) ((sem)->count = value) + +/* + * Timer + */ + +#ifdef _WIN32 + #ifdef __cplusplus + #define _c_LINKC extern "C" __declspec(dllimport) + #else + #define _c_LINKC __declspec(dllimport) + #endif + #if 1 // _WIN32_WINNT < _WIN32_WINNT_WIN8 || defined __TINYC__ + #define _c_getsystime GetSystemTimeAsFileTime + #else + #define _c_getsystime GetSystemTimePreciseAsFileTime + #endif + struct _FILETIME; + _c_LINKC void _c_getsystime(struct _FILETIME*); + _c_LINKC void Sleep(unsigned long); + + static inline double cco_time(void) { /* seconds since epoch */ + unsigned long long quad; /* 64-bit value representing 1/10th usecs since Jan 1 1601, 00:00 UTC */ + _c_getsystime((struct _FILETIME*)&quad); + return (double)(quad - 116444736000000000ULL)*1e-7; /* time diff Jan 1 1601-Jan 1 1970 in 1/10th usecs */ + } + + static inline void cco_sleep(double sec) { + Sleep((unsigned long)(sec*1000.0)); + } +#else + #include + static inline double cco_time(void) { /* seconds since epoch */ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec*1e-6; + } + + static inline void cco_sleep(double sec) { + struct timeval tv; + tv.tv_sec = (time_t)sec; + tv.tv_usec = (suseconds_t)((sec - (double)(long)sec)*1e6); + select(0, NULL, NULL, NULL, &tv); + } +#endif + +typedef struct { double interval, start; } cco_timer; + +#define cco_timer_await(tm, sec) cco_timer_await_v_3(tm, sec, CCO_AWAIT) +#define cco_timer_await_v(...) c_MACRO_OVERLOAD(cco_timer_await_v, __VA_ARGS__) +#define cco_timer_await_v_2(tm, sec) cco_timer_await_v_3(tm, sec, ) +#define cco_timer_await_v_3(tm, sec, ret) \ + do { \ + cco_timer_start(tm, sec); \ + cco_await_v_2(cco_timer_expired(tm), ret); \ + } while (0) + +static inline void cco_timer_start(cco_timer* tm, double sec) { + tm->interval = sec; + tm->start = cco_time(); +} + +static inline cco_timer cco_timer_from(double sec) { + cco_timer tm = {.interval=sec, .start=cco_time()}; + return tm; +} + +static inline void cco_timer_restart(cco_timer* tm) { + tm->start = cco_time(); +} + +static inline bool cco_timer_expired(cco_timer* tm) { + return cco_time() - tm->start >= tm->interval; +} + +static inline double cco_timer_elapsed(cco_timer* tm) { + return cco_time() - tm->start; +} + +static inline double cco_timer_remaining(cco_timer* tm) { + return tm->start + tm->interval - cco_time(); +} + +#endif -- cgit v1.2.3 From dbcc13635402bd466675f4f41e865d02abc6f918 Mon Sep 17 00:00:00 2001 From: tylov Date: Fri, 21 Jul 2023 10:49:45 +0200 Subject: NB! Changed some coroutine API for consistency/simplicity: Added full task support. --- docs/ccommon_api.md | 30 ++++++-------- include/stc/ccommon.h | 2 +- include/stc/coroutine.h | 44 ++++++++++---------- misc/examples/coroutines/cointerleave.c | 2 +- misc/examples/coroutines/coread.c | 2 +- misc/examples/coroutines/coroutines.c | 8 ++-- misc/examples/coroutines/cotasks1.c | 2 +- misc/examples/coroutines/cotasks2.c | 4 +- misc/examples/smartpointers/map_box.c | 34 +++++++++++++++ misc/examples/smartpointers/map_ptr.c | 34 +++++++++++++++ misc/examples/smartpointers/rawptr_elements.c | 59 --------------------------- misc/examples/spans/mdspan.c | 12 +++--- 12 files changed, 119 insertions(+), 114 deletions(-) create mode 100644 misc/examples/smartpointers/map_box.c create mode 100644 misc/examples/smartpointers/map_ptr.c delete mode 100644 misc/examples/smartpointers/rawptr_elements.c (limited to 'include/stc/coroutine.h') diff --git a/docs/ccommon_api.md b/docs/ccommon_api.md index 9189d7e8..0752beb5 100644 --- a/docs/ccommon_api.md +++ b/docs/ccommon_api.md @@ -374,36 +374,32 @@ cco_routine scope; Use `if-else-if` constructs instead. | | Function / operator | Description | |:----------|:-------------------------------------|:----------------------------------------| -| | Function / 'keywords': | | -|`cco_result` | Enum `CCO_DONE=0`, `CCO_YIELD`, `CCO_AWAIT` | Recommended return values in coroutines | -| | Function / 'keywords': | | +|`cco_result` | `CCO_DONE`, `CCO_AWAIT`, `CCO_YIELD` | Default set of return values from coroutines | | | `cco_cleanup:` | Label for cleanup position in coroutine | | `bool` | `cco_done(co)` | Is coroutine done? | -| | `cco_routine(co) { }` | The coroutine scope | +| | `cco_routine(co) {}` | The coroutine scope | | | `cco_yield();` | Yield/suspend execution (return CCO_YIELD)| -| | `cco_yield_v();` | Yield/suspend execution (return void) | | | `cco_yield_v(ret);` | Yield/suspend execution (return ret) | -| | `cco_yield_final();` | Yield final time, enables cleanup-state | -| | `cco_yield_final(ret);` | Yield a final value (e.g. CCO_ERROR) | +| | `cco_yield_final();` | Yield final suspend, enter cleanup-state | +| | `cco_yield_final(ret);` | Yield a final value | | | `cco_await(condition);` | Suspend until condition is true (return CCO_AWAIT)| -| | `cco_await_v(condition);` | Suspend until condition is true (return void) | -| | `cco_await_v(condition, ret);` | Suspend until condition is true (return ret)| -| | `cco_await_on(cocall);` | Await on sub-coroutine to finish (return its ret) | +| | `cco_call_await(cocall);` | Await for subcoro to finish (returns its ret value) | +| | `cco_call_await(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | | | `cco_return;` | Return from coroutine (inside cco_routine) | | | Task objects: | | | | `cco_task_struct(Name, ...);` | Define a coroutine task struct | -| | `cco_task_await(task, ...);` | Await for task to finish or optionally yield a value | +| | `cco_task_await(task, cco_runtime* rt);`| Await for task to finish | +| | `cco_task_await(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | +|`cco_result`| `cco_task_resume(task, rt);` | Resume suspended task | | | Semaphores: | | | | `cco_sem` | Semaphore type | | `cco_sem` | `cco_sem_from(long value)` | Create semaphore | | | `cco_sem_set(sem, long value)` | Set semaphore value | | | `cco_sem_await(sem)` | Await for the semaphore count > 0 | -| | `cco_sem_await(sem, ret)` | Await with ret on the semaphore | | | `cco_sem_release(sem)` | Signal the semaphore (count += 1) | | | Timers: | | | | `cco_timer` | Timer type | | | `cco_timer_await(tm, double sec)` | Await secs for timer to expire (usec prec.)| -| | `cco_timer_await(tm, double sec, ret)`| Await secs for timer with ret value | | | `cco_timer_start(tm, double sec)` | Start timer for secs duration | | | `cco_timer_restart(tm)` | Restart timer with same duration | | `bool` | `cco_timer_expired(tm)` | Return true if timer is expired | @@ -412,10 +408,10 @@ cco_routine scope; Use `if-else-if` constructs instead. | | From caller side: | | | `void` | `cco_stop(co)` | Next call of coroutine finalizes | | `void` | `cco_reset(co)` | Reset state to initial (for reuse) | -| `void` | `cco_block_on(cocall) { }` | Run blocking until cocall is finished | -| `void` | `cco_block_on(cocall, int *result) {}`| Run blocking until cocall is finished | -| | `cco_task_block_on(task) {}` | Run blocking until task is finished | -| | `cco_task_block_on(task, rt, STACKSZ) {}`| Run blocking until task is finished | +| `void` | `cco_call_blocking(cocall) {}` | Run blocking until cocall is finished | +| `void` | `cco_call_blocking(cocall, int* outres) {}`| Run blocking until cocall is finished | +| | `cco_task_blocking(task) {}` | Run blocking until task is finished | +| | `cco_task_blocking(task, rt, STACKSZ) {}`| Run blocking until task is finished | | | Time functions: | | | `double` | `cco_time(void)` | Return secs with usec prec. since Epoch | | | `cco_sleep(double sec)` | Sleep for seconds (msec or usec prec.) | diff --git a/include/stc/ccommon.h b/include/stc/ccommon.h index 1f9ea80d..316a8ee7 100644 --- a/include/stc/ccommon.h +++ b/include/stc/ccommon.h @@ -69,7 +69,7 @@ typedef long long _llong; #define c_new(T, ...) ((T*)memcpy(malloc(sizeof(T)), ((T[]){__VA_ARGS__}), sizeof(T))) #define c_LITERAL(T) (T) #endif -#define c_new_n(T, n) ((T*)malloc(sizeof(T)*(n))) +#define c_new_n(T, n) ((T*)malloc(sizeof(T)*(size_t)(n))) #define c_malloc(sz) malloc(c_i2u(sz)) #define c_calloc(n, sz) calloc(c_i2u(n), c_i2u(sz)) #define c_realloc(p, sz) realloc(p, c_i2u(sz)) diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index f89d20af..42905744 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -83,26 +83,27 @@ typedef enum { case __LINE__:; \ } while (0) -#define cco_await(promise) cco_await_v_2(promise, CCO_AWAIT) -#define cco_await_v(...) c_MACRO_OVERLOAD(cco_await_v, __VA_ARGS__) -#define cco_await_v_1(promise) cco_await_v_2(promise, ) -#define cco_await_v_2(promise, ret) \ +#define cco_await(promise) cco_await_and_return(promise, CCO_AWAIT) +#define cco_await_v(promise) cco_await_and_return(promise, ) +#define cco_await_and_return(promise, ret) \ do { \ *_state = __LINE__; \ case __LINE__: if (!(promise)) {return ret; goto _resume;} \ } while (0) -/* cco_await_on(): assumes coroutine returns a cco_result value (int) */ -#define cco_await_on(corocall) \ +/* cco_call_await(): assumes coroutine returns a cco_result value (int) */ +#define cco_call_await(...) c_MACRO_OVERLOAD(cco_call_await, __VA_ARGS__) +#define cco_call_await_1(corocall) cco_call_await_2(corocall, CCO_DONE) +#define cco_call_await_2(corocall, resultbits) \ do { \ *_state = __LINE__; \ - case __LINE__: { int _r = corocall; if (_r != CCO_DONE) {return _r; goto _resume;} } \ + case __LINE__: { int _r = corocall; if (!(_r & ~(resultbits))) {return _r; goto _resume;} } \ } while (0) -/* cco_block_on(): assumes coroutine returns a cco_result value (int) */ -#define cco_block_on(...) c_MACRO_OVERLOAD(cco_block_on, __VA_ARGS__) -#define cco_block_on_1(corocall) while ((corocall) != CCO_DONE) -#define cco_block_on_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) +/* cco_call_blocking(): assumes coroutine returns a cco_result value (int) */ +#define cco_call_blocking(...) c_MACRO_OVERLOAD(cco_call_blocking, __VA_ARGS__) +#define cco_call_blocking_1(corocall) while ((corocall) != CCO_DONE) +#define cco_call_blocking_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) #define cco_cleanup \ *_state = CCO_STATE_CLEANUP; case CCO_STATE_CLEANUP @@ -152,7 +153,7 @@ typedef struct cco_runtime { #define cco_cast_task(task) \ ((cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state))) -#define cco_resume(task, rt) \ +#define cco_task_resume(task, rt) \ (task)->cco_func(task, rt) #define cco_task_await(...) c_MACRO_OVERLOAD(cco_task_await, __VA_ARGS__) @@ -164,11 +165,11 @@ typedef struct cco_runtime { cco_yield_v(CCO_AWAIT); \ } while (0) -#define cco_task_block_on(...) c_MACRO_OVERLOAD(cco_task_block_on, __VA_ARGS__) -#define cco_task_block_on_1(task) cco_task_block_on_3(task, _rt, 16) -#define cco_task_block_on_3(task, rt, STACKDEPTH) \ +#define cco_task_blocking(...) c_MACRO_OVERLOAD(cco_task_blocking, __VA_ARGS__) +#define cco_task_blocking_1(task) cco_task_blocking_3(task, _rt, 16) +#define cco_task_blocking_3(task, rt, STACKDEPTH) \ for (struct { int result, top; cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \ - (((rt.result = cco_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) + (((rt.result = cco_task_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) /* * Semaphore @@ -176,12 +177,11 @@ typedef struct cco_runtime { typedef struct { intptr_t count; } cco_sem; -#define cco_sem_await(sem) cco_sem_await_v_2(sem, CCO_AWAIT) -#define cco_sem_await_v(...) c_MACRO_OVERLOAD(cco_sem_await_v, __VA_ARGS__) -#define cco_sem_await_v_1(sem) cco_sem_await_v_2(sem, ) -#define cco_sem_await_v_2(sem, ret) \ +#define cco_sem_await(sem) cco_sem_await_and_return(sem, CCO_AWAIT) +#define cco_sem_await_v(sem) cco_sem_await_and_return(sem, ) +#define cco_sem_await_and_return(sem, ret) \ do { \ - cco_await_v_2((sem)->count > 0, ret); \ + cco_await_and_return((sem)->count > 0, ret); \ --(sem)->count; \ } while (0) @@ -241,7 +241,7 @@ typedef struct { double interval, start; } cco_timer; #define cco_timer_await_v_3(tm, sec, ret) \ do { \ cco_timer_start(tm, sec); \ - cco_await_v_2(cco_timer_expired(tm), ret); \ + cco_await_and_return(cco_timer_expired(tm), ret); \ } while (0) static inline void cco_timer_start(cco_timer* tm, double sec) { diff --git a/misc/examples/coroutines/cointerleave.c b/misc/examples/coroutines/cointerleave.c index ea0d4dac..f3710ba3 100644 --- a/misc/examples/coroutines/cointerleave.c +++ b/misc/examples/coroutines/cointerleave.c @@ -49,7 +49,7 @@ void Use(void) struct Generator g = {{&a}, {&b}}; - cco_block_on(interleaved(&g)) { + cco_call_blocking(interleaved(&g)) { printf("%d ", g.value); } puts(""); diff --git a/misc/examples/coroutines/coread.c b/misc/examples/coroutines/coread.c index 56248108..ebaaf19d 100644 --- a/misc/examples/coroutines/coread.c +++ b/misc/examples/coroutines/coread.c @@ -33,7 +33,7 @@ int main(void) { struct file_read g = {__FILE__}; int n = 0; - cco_block_on(file_read(&g)) + cco_call_blocking(file_read(&g)) { printf("%3d %s\n", ++n, cstr_str(&g.line)); //if (n == 10) cco_stop(&g); diff --git a/misc/examples/coroutines/coroutines.c b/misc/examples/coroutines/coroutines.c index de0fcda5..489c3ed6 100644 --- a/misc/examples/coroutines/coroutines.c +++ b/misc/examples/coroutines/coroutines.c @@ -84,13 +84,13 @@ struct combined { int combined(struct combined* g) { cco_routine(g) { - cco_await_on(prime(&g->prm)); - cco_await_on(fibonacci(&g->fib)); + cco_call_await(prime(&g->prm)); + cco_call_await(fibonacci(&g->fib)); // Reuse the g->prm context and extend the count: g->prm.count = 8, g->prm.result += 2; cco_reset(&g->prm); - cco_await_on(prime(&g->prm)); + cco_call_await(prime(&g->prm)); cco_cleanup: puts("final combined"); @@ -103,7 +103,7 @@ int main(void) struct combined c = {.prm={.count=8}, .fib={14}}; int res; - cco_block_on(combined(&c), &res) { + cco_call_blocking(combined(&c), &res) { if (res == CCO_YIELD) printf("Prime(%d)=%lld, Fib(%d)=%lld\n", c.prm.idx, c.prm.result, diff --git a/misc/examples/coroutines/cotasks1.c b/misc/examples/coroutines/cotasks1.c index 27999ccf..e4afbe2b 100644 --- a/misc/examples/coroutines/cotasks1.c +++ b/misc/examples/coroutines/cotasks1.c @@ -88,7 +88,7 @@ int main(void) struct consume_items consume = {.n=5}; int count = 0; - cco_block_on(consume_items(&consume, &produce)) + cco_call_blocking(consume_items(&consume, &produce)) { ++count; //cco_sleep(0.001); diff --git a/misc/examples/coroutines/cotasks2.c b/misc/examples/coroutines/cotasks2.c index 9ca69bda..24a9f23f 100644 --- a/misc/examples/coroutines/cotasks2.c +++ b/misc/examples/coroutines/cotasks2.c @@ -77,7 +77,7 @@ int consume_items(struct consume_items* c, cco_runtime* rt) } cco_cleanup: cco_stop(&c->produce); - cco_resume(&c->produce, rt); + cco_task_resume(&c->produce, rt); puts("done consume"); } return 0; @@ -92,7 +92,7 @@ int main(void) }; int count = 0; - cco_task_block_on(&consume) + cco_task_blocking(&consume) { ++count; //cco_sleep(0.001); diff --git a/misc/examples/smartpointers/map_box.c b/misc/examples/smartpointers/map_box.c new file mode 100644 index 00000000..f651b302 --- /dev/null +++ b/misc/examples/smartpointers/map_box.c @@ -0,0 +1,34 @@ +#include +#include +#define i_implement +#include + +#define i_type IBox +#define i_key long +#include // unique_ptr alike. + +// cmap of cstr => IBox +#define i_type Boxmap +#define i_key_str +#define i_valboxed IBox // i_valboxed: use properties from IBox automatically +#include + + +int main(void) +{ + Boxmap map = {0}; + + puts("Map cstr => IBox:"); + Boxmap_insert(&map, cstr_from("Test1"), IBox_make(1)); + Boxmap_insert(&map, cstr_from("Test2"), IBox_make(2)); + + // Simpler: emplace() implicitly creates cstr from const char* and IBox from long! + Boxmap_emplace(&map, "Test3", 3); + Boxmap_emplace(&map, "Test4", 4); + + c_forpair (name, number, Boxmap, map) + printf("%s: %ld\n", cstr_str(_.name), *_.number->get); + puts(""); + + Boxmap_drop(&map); +} diff --git a/misc/examples/smartpointers/map_ptr.c b/misc/examples/smartpointers/map_ptr.c new file mode 100644 index 00000000..453322c5 --- /dev/null +++ b/misc/examples/smartpointers/map_ptr.c @@ -0,0 +1,34 @@ +#include +#include +#define i_implement +#include + +// cmap of cstr => long* +#define i_type Ptrmap +#define i_key_str +#define i_val long* +#define i_valraw long +#define i_valfrom(raw) c_new(long, raw) +#define i_valto(x) **x +#define i_valclone(x) c_new(long, *x) +#define i_valdrop(x) c_free(*x) +#include + +int main(void) +{ + Ptrmap map = {0}; + + puts("Map cstr => long*:"); + Ptrmap_insert(&map, cstr_from("Test1"), c_new(long, 1)); + Ptrmap_insert(&map, cstr_from("Test2"), c_new(long, 2)); + + // Simple: emplace() implicitly creates cstr from const char* and an owned long* from long! + Ptrmap_emplace(&map, "Test3", 3); + Ptrmap_emplace(&map, "Test4", 4); + + c_forpair (name, number, Ptrmap, map) + printf("%s: %ld\n", cstr_str(_.name), **_.number); + puts(""); + + Ptrmap_drop(&map); +} diff --git a/misc/examples/smartpointers/rawptr_elements.c b/misc/examples/smartpointers/rawptr_elements.c deleted file mode 100644 index 694ce12e..00000000 --- a/misc/examples/smartpointers/rawptr_elements.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#define i_implement -#include - -// Create cmap of cstr => long* -#define i_type SIPtrMap -#define i_key_str -#define i_val long* -#define i_valraw long -#define i_valfrom(raw) c_new(long, raw) -#define i_valto(x) **x -#define i_valclone(x) c_new(long, *x) -#define i_valdrop(x) c_free(*x) -#include - -// Alternatively, using cbox: -#define i_type IBox -#define i_key long -#include // unique_ptr alike. - -// cmap of cstr => IBox -#define i_type SIBoxMap -#define i_key_str -#define i_valboxed IBox // i_valboxed: use properties from IBox automatically -#include - -int main(void) -{ - // These have the same behaviour, except IBox has a get member: - SIPtrMap map1 = {0}; - SIBoxMap map2 = {0}; - - printf("\nMap cstr => long*:\n"); - SIPtrMap_insert(&map1, cstr_from("Test1"), c_new(long, 1)); - SIPtrMap_insert(&map1, cstr_from("Test2"), c_new(long, 2)); - - // Emplace implicitly creates cstr from const char* and an owned long* from long! - SIPtrMap_emplace(&map1, "Test3", 3); - SIPtrMap_emplace(&map1, "Test4", 4); - - c_forpair (name, number, SIPtrMap, map1) - printf("%s: %ld\n", cstr_str(_.name), **_.number); - - puts("\nMap cstr => IBox:"); - SIBoxMap_insert(&map2, cstr_from("Test1"), IBox_make(1)); - SIBoxMap_insert(&map2, cstr_from("Test2"), IBox_make(2)); - - // Emplace implicitly creates cstr from const char* and IBox from long! - SIBoxMap_emplace(&map2, "Test3", 3); - SIBoxMap_emplace(&map2, "Test4", 4); - - c_forpair (name, number, SIBoxMap, map2) - printf("%s: %ld\n", cstr_str(_.name), *_.number->get); - puts(""); - - SIPtrMap_drop(&map1); - SIBoxMap_drop(&map2); -} diff --git a/misc/examples/spans/mdspan.c b/misc/examples/spans/mdspan.c index 4427299c..db601850 100644 --- a/misc/examples/spans/mdspan.c +++ b/misc/examples/spans/mdspan.c @@ -12,17 +12,17 @@ int main(void) { DSpan3 ms = cspan_md_order('F', data, nx, ny, nz); // Fortran, not 'C' int idx = 0; - c_forrange (i, ms.shape[0]) - c_forrange (j, ms.shape[1]) - c_forrange (k, ms.shape[2]) + for (int i = 0; i < ms.shape[0]; ++i) + for (int j = 0; j < ms.shape[1]; ++j) + for (int k = 0; k < ms.shape[2]; ++k) *cspan_at(&ms, i, j, k) = ++idx; cspan_transpose(&ms); printf(", transposed:\n\n"); - c_forrange (i, ms.shape[0]) { - c_forrange (j, ms.shape[1]) { - c_forrange (k, ms.shape[2]) + for (int i = 0; i < ms.shape[0]; ++i) { + for (int j = 0; j < ms.shape[1]; ++j) { + for (int k = 0; k < ms.shape[2]; ++k) printf(" %3g", *cspan_at(&ms, i, j, k)); puts(""); } -- cgit v1.2.3 From a64d171f0eb76c8f208ffc7d8501baa8222634d3 Mon Sep 17 00:00:00 2001 From: tylov Date: Thu, 27 Jul 2023 15:24:46 +0200 Subject: An improvement in cvec/cdeq insert_uninit() impl. --- include/stc/cdeq.h | 2 +- include/stc/coroutine.h | 3 ++- include/stc/cvec.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/include/stc/cdeq.h b/include/stc/cdeq.h index cde2928c..b0575fe0 100644 --- a/include/stc/cdeq.h +++ b/include/stc/cdeq.h @@ -156,7 +156,7 @@ _cx_MEMB(_insert_uninit)(_cx_Self* self, const intptr_t idx, const intptr_t n) { const intptr_t len = _cx_MEMB(_size)(self); _cx_iter it = {._s=self}; if (len + n > self->capmask) - if (!_cx_MEMB(_reserve)(self, len + n)) + if (!_cx_MEMB(_reserve)(self, len*5/4 + n)) return it; for (intptr_t i = len - 1, j = i + n; i >= idx; --i, --j) *_cx_MEMB(_at_mut)(self, j) = *_cx_MEMB(_at)(self, i); diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 42905744..9f55dddf 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -169,7 +169,8 @@ typedef struct cco_runtime { #define cco_task_blocking_1(task) cco_task_blocking_3(task, _rt, 16) #define cco_task_blocking_3(task, rt, STACKDEPTH) \ for (struct { int result, top; cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \ - (((rt.result = cco_task_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) + (((rt.result = cco_task_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & \ + ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) /* * Semaphore diff --git a/include/stc/cvec.h b/include/stc/cvec.h index 774f0582..e2b8fe97 100644 --- a/include/stc/cvec.h +++ b/include/stc/cvec.h @@ -326,7 +326,7 @@ _cx_MEMB(_push)(_cx_Self* self, i_key value) { STC_DEF _cx_iter _cx_MEMB(_insert_uninit)(_cx_Self* self, const intptr_t idx, const intptr_t n) { if (self->_len + n > self->_cap) - if (!_cx_MEMB(_reserve)(self, self->_len*3/2 + n)) + if (!_cx_MEMB(_reserve)(self, self->_len*5/4 + n)) return _cx_MEMB(_end)(self); _cx_value* pos = self->data + idx; -- cgit v1.2.3 From e6003c0ecfd7e76803bac7d98feca814997e8a86 Mon Sep 17 00:00:00 2001 From: tylov Date: Sun, 30 Jul 2023 22:46:33 +0200 Subject: Added cco_generator(Name, ...) macro to simplify defining the iterator struct. Updated example generator.c --- include/stc/coroutine.h | 14 +++++++++++++- misc/examples/coroutines/generator.c | 19 +++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 9f55dddf..7917878a 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -132,7 +132,19 @@ typedef enum { (void)((co)->cco_state = 0) /* - * Tasks (optional) + * Generators + */ + +#define cco_generator(Name, ...) \ + typedef Name Name##_value; \ + typedef struct { \ + Name##_value* ref; \ + int cco_state; \ + __VA_ARGS__ \ + } Name##_iter + +/* + * Tasks */ struct cco_runtime; diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c index f9e59fea..c092b92d 100644 --- a/misc/examples/coroutines/generator.c +++ b/misc/examples/coroutines/generator.c @@ -6,19 +6,17 @@ typedef struct { int size; int a, b, c; -} Triple, Triple_value; +} Triple; -typedef struct { - Triple_value* ref; +cco_generator(Triple, int count; - int cco_state; -} Triple_iter; +); int Triple_next(Triple_iter* it) { - Triple_value* g = it->ref; + Triple* g = it->ref; // note: before cco_routine cco_routine(it) { - for (g->c = 5; g->size; ++g->c) { + for (g->c = 5;; ++g->c) { for (g->a = 1; g->a < g->c; ++g->a) { for (g->b = g->a; g->b < g->c; ++g->b) { if (g->a*g->a + g->b*g->b == g->c*g->c) { @@ -31,12 +29,13 @@ int Triple_next(Triple_iter* it) { } cco_cleanup: it->ref = NULL; + puts("done"); } return 0; } Triple_iter Triple_begin(Triple* g) { - Triple_iter it = {.ref=g}; + Triple_iter it = {.ref=g}; Triple_next(&it); return it; } @@ -44,8 +43,8 @@ Triple_iter Triple_begin(Triple* g) { int main(void) { - puts("Pythagorean triples with c < 100:"); - Triple triple = {.size=30}; // max number of triples + puts("Pythagorean triples; stops at 100 triples or c >= 100:"); + Triple triple = {.size=100}; c_foreach (i, Triple, triple) { if (i.ref->c < 100) printf("%u: (%d, %d, %d)\n", i.count, i.ref->a, i.ref->b, i.ref->c); -- cgit v1.2.3 From cd9d0f984e678691855787a2fa855ccd5876425f Mon Sep 17 00:00:00 2001 From: tylov Date: Mon, 31 Jul 2023 08:37:59 +0200 Subject: Renamed cco_generator() => cco_iter_struct(). More in line with cco_task_struct() macro. --- include/stc/coroutine.h | 2 +- misc/examples/coroutines/generator.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 7917878a..146f3efb 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -135,7 +135,7 @@ typedef enum { * Generators */ -#define cco_generator(Name, ...) \ +#define cco_iter_struct(Name, ...) \ typedef Name Name##_value; \ typedef struct { \ Name##_value* ref; \ diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c index c092b92d..3f51ce9c 100644 --- a/misc/examples/coroutines/generator.c +++ b/misc/examples/coroutines/generator.c @@ -8,7 +8,7 @@ typedef struct { int a, b, c; } Triple; -cco_generator(Triple, +cco_iter_struct(Triple, int count; ); -- cgit v1.2.3 From fc0942205fb06591a5784b2878187a4475c6af7b Mon Sep 17 00:00:00 2001 From: tylov Date: Tue, 1 Aug 2023 15:45:37 +0200 Subject: Fixed bug in cco_call_await() test. --- include/stc/coroutine.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 146f3efb..4a7fd6aa 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -94,10 +94,10 @@ typedef enum { /* cco_call_await(): assumes coroutine returns a cco_result value (int) */ #define cco_call_await(...) c_MACRO_OVERLOAD(cco_call_await, __VA_ARGS__) #define cco_call_await_1(corocall) cco_call_await_2(corocall, CCO_DONE) -#define cco_call_await_2(corocall, resultbits) \ +#define cco_call_await_2(corocall, awaitbits) \ do { \ *_state = __LINE__; \ - case __LINE__: { int _r = corocall; if (!(_r & ~(resultbits))) {return _r; goto _resume;} } \ + case __LINE__: { int _r = corocall; if (_r & ~(awaitbits)) {return _r; goto _resume;} } \ } while (0) /* cco_call_blocking(): assumes coroutine returns a cco_result value (int) */ @@ -170,10 +170,10 @@ typedef struct cco_runtime { #define cco_task_await(...) c_MACRO_OVERLOAD(cco_task_await, __VA_ARGS__) #define cco_task_await_2(task, rt) cco_task_await_3(task, rt, CCO_DONE) -#define cco_task_await_3(task, rt, resultbits) \ +#define cco_task_await_3(task, rt, awaitbits) \ do { \ cco_runtime* _rt = rt; \ - (_rt->stack[++_rt->top] = cco_cast_task(task))->cco_expect = (resultbits); \ + (_rt->stack[++_rt->top] = cco_cast_task(task))->cco_expect = (awaitbits); \ cco_yield_v(CCO_AWAIT); \ } while (0) -- cgit v1.2.3 From 94391527ef50cbee073a4b427f6fe839c010ecb1 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Tue, 1 Aug 2023 16:47:38 +0200 Subject: Last minute API change for coroutines before V4.3 release: - Renamed cco_xxxx_await() => cco_await_xxxx() (call, task, sem, timer) - Renamed cco_xxxx_blocking() => cco_blocking_xxxx() (call, task) - Renamed cco_task_resume() => cco_resume_task() - Simplified cco_blocking_call() --- docs/coroutine_api.md | 46 +++++++++++++------------- include/stc/coroutine.h | 44 ++++++++++++------------ misc/examples/coroutines/cointerleave.c | 2 +- misc/examples/coroutines/coread.c | 2 +- misc/examples/coroutines/coroutines.c | 8 ++--- misc/examples/coroutines/cotasks1.c | 4 +-- misc/examples/coroutines/cotasks2.c | 10 +++--- misc/examples/coroutines/dining_philosophers.c | 14 ++++---- misc/examples/coroutines/filetask.c | 6 ++-- misc/examples/coroutines/triples.c | 2 +- 10 files changed, 68 insertions(+), 70 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/docs/coroutine_api.md b/docs/coroutine_api.md index 30b85640..b356dcf0 100644 --- a/docs/coroutine_api.md +++ b/docs/coroutine_api.md @@ -24,23 +24,24 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s | | `cco_yield_final();` | Yield final suspend, enter cleanup-state | | | `cco_yield_final(ret);` | Yield a final value | | | `cco_await(condition);` | Suspend until condition is true (return CCO_AWAIT)| -| | `cco_call_await(cocall);` | Await for subcoro to finish (returns its ret value) | -| | `cco_call_await(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | +| | `cco_await_call(cocall);` | Await for subcoro to finish (returns its ret value) | +| | `cco_await_call(cocall, retbit);` | Await for subcoro's return to be in (retbit \| CCO_DONE) | | | `cco_return;` | Return from coroutine (inside cco_routine) | | | Task objects: | | | | `cco_task_struct(Name, ...);` | Define a coroutine task struct | -| | `cco_task_await(task, cco_runtime* rt);`| Await for task to finish | -| | `cco_task_await(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | -|`cco_result`| `cco_task_resume(task, rt);` | Resume suspended task | +| | `cco_await_task(task, cco_runtime* rt);`| Await for task to finish | +| | `cco_await_task(task, rt, retbit);` | Await for task's return to be in (retbit \| CCO_DONE) | +|`cco_result`| `cco_resume_task(task, rt);` | Resume suspended task | | | Semaphores: | | | | `cco_sem` | Semaphore type | +| | `cco_await_sem(sem)` | Await for the semaphore count > 0 | | `cco_sem` | `cco_sem_from(long value)` | Create semaphore | | | `cco_sem_set(sem, long value)` | Set semaphore value | -| | `cco_sem_await(sem)` | Await for the semaphore count > 0 | + | | `cco_sem_release(sem)` | Signal the semaphore (count += 1) | | | Timers: | | | | `cco_timer` | Timer type | -| | `cco_timer_await(tm, double sec)` | Await secs for timer to expire (usec prec.)| +| | `cco_await_timer(tm, double sec)` | Await secs for timer to expire (usec prec.)| | | `cco_timer_start(tm, double sec)` | Start timer for secs duration | | | `cco_timer_restart(tm)` | Restart timer with same duration | | `bool` | `cco_timer_expired(tm)` | Return true if timer is expired | @@ -49,10 +50,9 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s | | From caller side: | | | `void` | `cco_stop(co)` | Next call of coroutine finalizes | | `void` | `cco_reset(co)` | Reset state to initial (for reuse) | -| `void` | `cco_call_blocking(cocall) {}` | Run blocking until cocall is finished | -| `void` | `cco_call_blocking(cocall, int* outres) {}`| Run blocking until cocall is finished | -| | `cco_task_blocking(task) {}` | Run blocking until task is finished | -| | `cco_task_blocking(task, rt, STACKSZ) {}`| Run blocking until task is finished | +| `void` | `cco_blocking_call(cocall) {}` | Run blocking until cocall is finished | +| | `cco_blocking_task(task) {}` | Run blocking until task is finished | +| | `cco_blocking_task(task, rt, STACKSZ) {}`| Run blocking until task is finished | | | Time functions: | | | `double` | `cco_time(void)` | Return secs with usec prec. since Epoch | | | `cco_sleep(double sec)` | Sleep for seconds (msec or usec prec.) | @@ -117,7 +117,7 @@ int main(void) { struct triples co = {.max_c = 25}; int n = 0; - cco_call_blocking(triples(&co)) { + cco_blocking_call(triples(&co)) { printf("%d: [%d, %d, %d]\n", ++n, co.a, co.b, co.c); } } @@ -171,26 +171,26 @@ int main(void) { struct gcd1_triples co = {.max_n = 100, .max_c = 100}; int n = 0; - cco_call_blocking(gcd1_triples(&co)) { + cco_blocking_call(gcd1_triples(&co)) { printf("%d: [%d, %d, %d]\n", ++n, co.tri.a, co.tri.b, co.tri.c); } } ``` -When using ***cco_call_blocking()***, the coroutine is continuously resumed after each yield suspension. +When using ***cco_blocking_call()***, the coroutine is continuously resumed after each yield suspension. However, this means that it first calls ***gcd1_triples()***, which immediately jumps to after the `cco_yield` -statement and calls ***triples()***, which again jumps and resumes after its `cco_yield`-statement. This is efficient only when yielding or awaiting from the top- or second-level call like here, but naturally not when couroutine calls are more deeply nested or recursive. The STC coroutine implementation therefore also contains task-objects (`cco_task`), which are base-coroutine -objects/enclosures. These can be executed using ***cco_task_blocking()*** instead of ***cco_call_blocking()***. -Inner coroutine calls are done by ***cco_task_await()***, where you may await for a certain return value, normally CCO_YIELD or just CCO_DONE. It uses a stack of pointers of task-enclosures to call the current +objects/enclosures. These can be executed using ***cco_blocking_task()*** instead of ***cco_blocking_call()***. +Inner coroutine calls are done by ***cco_await_task()***, where you may await for a certain return value, normally CCO_YIELD or just CCO_DONE. It uses a stack of pointers of task-enclosures to call the current inner-level function directly. The task-objects have the added benefit that coroutines can be managed by a scheduler, which is useful when dealing with large numbers of coroutines (like in many games and simulations). Note that these two modes may be mixed, and that for short-lived coroutines (only a few suspends), -it is often beneficial to call the sub-coroutine directly rather than via ***cco_task_await()*** +it is often beneficial to call the sub-coroutine directly rather than via ***cco_await_task()*** (which pushes the task on top of the runtime stack and yields - then executed on next resume). The following example uses task-objects with 3-levels deep coroutine calls. It emulates an async generator @@ -212,7 +212,7 @@ int next_value(struct next_value* co, cco_runtime* rt) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); // suspend with CCO_AWAIT + cco_await_timer(&co->tm, 1 + rand() % 2); // suspend with CCO_AWAIT co->val = rand(); cco_yield(); // suspend with CCO_YIELD } @@ -242,7 +242,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) while (true) { // await for CCO_YIELD (or CCO_DONE) - cco_task_await(&p->next, rt, CCO_YIELD); + cco_await_task(&p->next, rt, CCO_YIELD); cstr_printf(&p->str, "item %d", p->next.val); print_time(); printf("produced %s\n", cstr_str(&p->str)); @@ -269,13 +269,13 @@ int consume_items(struct consume_items* c, cco_runtime* rt) for (c->i = 1; c->i <= c->n; ++c->i) { printf("consume #%d\n", c->i); - cco_task_await(&c->produce, rt, CCO_YIELD); + cco_await_task(&c->produce, rt, CCO_YIELD); print_time(); printf("consumed %s\n", cstr_str(&c->produce.str)); } cco_cleanup: cco_stop(&c->produce); - cco_task_resume(&c->produce, rt); + cco_resume_task(&c->produce, rt); puts("done consume"); } return 0; @@ -287,6 +287,6 @@ int main(void) .n = 5, .cco_func = consume_items, }; - cco_task_blocking(&consume); + cco_blocking_task(&consume); } -``` \ No newline at end of file +``` diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 4a7fd6aa..0e592bae 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -91,19 +91,17 @@ typedef enum { case __LINE__: if (!(promise)) {return ret; goto _resume;} \ } while (0) -/* cco_call_await(): assumes coroutine returns a cco_result value (int) */ -#define cco_call_await(...) c_MACRO_OVERLOAD(cco_call_await, __VA_ARGS__) -#define cco_call_await_1(corocall) cco_call_await_2(corocall, CCO_DONE) -#define cco_call_await_2(corocall, awaitbits) \ +/* cco_await_call(): assumes coroutine returns a cco_result value (int) */ +#define cco_await_call(...) c_MACRO_OVERLOAD(cco_await_call, __VA_ARGS__) +#define cco_await_call_1(corocall) cco_await_call_2(corocall, CCO_DONE) +#define cco_await_call_2(corocall, awaitbits) \ do { \ *_state = __LINE__; \ case __LINE__: { int _r = corocall; if (_r & ~(awaitbits)) {return _r; goto _resume;} } \ } while (0) -/* cco_call_blocking(): assumes coroutine returns a cco_result value (int) */ -#define cco_call_blocking(...) c_MACRO_OVERLOAD(cco_call_blocking, __VA_ARGS__) -#define cco_call_blocking_1(corocall) while ((corocall) != CCO_DONE) -#define cco_call_blocking_2(corocall, result) while ((*(result) = (corocall)) != CCO_DONE) +/* cco_blocking_call(): assumes coroutine returns a cco_result value (int) */ +#define cco_blocking_call(corocall) while ((corocall) != CCO_DONE) #define cco_cleanup \ *_state = CCO_STATE_CLEANUP; case CCO_STATE_CLEANUP @@ -165,23 +163,23 @@ typedef struct cco_runtime { #define cco_cast_task(task) \ ((cco_task *)(task) + 0*sizeof((task)->cco_func(task, (cco_runtime*)0) + ((int*)0 == &(task)->cco_state))) -#define cco_task_resume(task, rt) \ +#define cco_resume_task(task, rt) \ (task)->cco_func(task, rt) -#define cco_task_await(...) c_MACRO_OVERLOAD(cco_task_await, __VA_ARGS__) -#define cco_task_await_2(task, rt) cco_task_await_3(task, rt, CCO_DONE) -#define cco_task_await_3(task, rt, awaitbits) \ +#define cco_await_task(...) c_MACRO_OVERLOAD(cco_await_task, __VA_ARGS__) +#define cco_await_task_2(task, rt) cco_await_task_3(task, rt, CCO_DONE) +#define cco_await_task_3(task, rt, awaitbits) \ do { \ cco_runtime* _rt = rt; \ (_rt->stack[++_rt->top] = cco_cast_task(task))->cco_expect = (awaitbits); \ cco_yield_v(CCO_AWAIT); \ } while (0) -#define cco_task_blocking(...) c_MACRO_OVERLOAD(cco_task_blocking, __VA_ARGS__) -#define cco_task_blocking_1(task) cco_task_blocking_3(task, _rt, 16) -#define cco_task_blocking_3(task, rt, STACKDEPTH) \ +#define cco_blocking_task(...) c_MACRO_OVERLOAD(cco_blocking_task, __VA_ARGS__) +#define cco_blocking_task_1(task) cco_blocking_task_3(task, _rt, 16) +#define cco_blocking_task_3(task, rt, STACKDEPTH) \ for (struct { int result, top; cco_task* stack[STACKDEPTH]; } rt = {.stack={cco_cast_task(task)}}; \ - (((rt.result = cco_task_resume(rt.stack[rt.top], (cco_runtime*)&rt)) & \ + (((rt.result = cco_resume_task(rt.stack[rt.top], (cco_runtime*)&rt)) & \ ~rt.stack[rt.top]->cco_expect) || --rt.top >= 0); ) /* @@ -190,9 +188,9 @@ typedef struct cco_runtime { typedef struct { intptr_t count; } cco_sem; -#define cco_sem_await(sem) cco_sem_await_and_return(sem, CCO_AWAIT) -#define cco_sem_await_v(sem) cco_sem_await_and_return(sem, ) -#define cco_sem_await_and_return(sem, ret) \ +#define cco_await_sem(sem) cco_await_sem_and_return(sem, CCO_AWAIT) +#define cco_await_sem_v(sem) cco_await_sem_and_return(sem, ) +#define cco_await_sem_and_return(sem, ret) \ do { \ cco_await_and_return((sem)->count > 0, ret); \ --(sem)->count; \ @@ -248,10 +246,10 @@ typedef struct { intptr_t count; } cco_sem; typedef struct { double interval, start; } cco_timer; -#define cco_timer_await(tm, sec) cco_timer_await_v_3(tm, sec, CCO_AWAIT) -#define cco_timer_await_v(...) c_MACRO_OVERLOAD(cco_timer_await_v, __VA_ARGS__) -#define cco_timer_await_v_2(tm, sec) cco_timer_await_v_3(tm, sec, ) -#define cco_timer_await_v_3(tm, sec, ret) \ +#define cco_await_timer(tm, sec) cco_await_timer_v_3(tm, sec, CCO_AWAIT) +#define cco_await_timer_v(...) c_MACRO_OVERLOAD(cco_await_timer_v, __VA_ARGS__) +#define cco_await_timer_v_2(tm, sec) cco_await_timer_v_3(tm, sec, ) +#define cco_await_timer_v_3(tm, sec, ret) \ do { \ cco_timer_start(tm, sec); \ cco_await_and_return(cco_timer_expired(tm), ret); \ diff --git a/misc/examples/coroutines/cointerleave.c b/misc/examples/coroutines/cointerleave.c index f3710ba3..80494176 100644 --- a/misc/examples/coroutines/cointerleave.c +++ b/misc/examples/coroutines/cointerleave.c @@ -49,7 +49,7 @@ void Use(void) struct Generator g = {{&a}, {&b}}; - cco_call_blocking(interleaved(&g)) { + cco_blocking_call(interleaved(&g)) { printf("%d ", g.value); } puts(""); diff --git a/misc/examples/coroutines/coread.c b/misc/examples/coroutines/coread.c index ebaaf19d..359ca85d 100644 --- a/misc/examples/coroutines/coread.c +++ b/misc/examples/coroutines/coread.c @@ -33,7 +33,7 @@ int main(void) { struct file_read g = {__FILE__}; int n = 0; - cco_call_blocking(file_read(&g)) + cco_blocking_call(file_read(&g)) { printf("%3d %s\n", ++n, cstr_str(&g.line)); //if (n == 10) cco_stop(&g); diff --git a/misc/examples/coroutines/coroutines.c b/misc/examples/coroutines/coroutines.c index 489c3ed6..faeb71f6 100644 --- a/misc/examples/coroutines/coroutines.c +++ b/misc/examples/coroutines/coroutines.c @@ -84,13 +84,13 @@ struct combined { int combined(struct combined* g) { cco_routine(g) { - cco_call_await(prime(&g->prm)); - cco_call_await(fibonacci(&g->fib)); + cco_await_call(prime(&g->prm)); + cco_await_call(fibonacci(&g->fib)); // Reuse the g->prm context and extend the count: g->prm.count = 8, g->prm.result += 2; cco_reset(&g->prm); - cco_call_await(prime(&g->prm)); + cco_await_call(prime(&g->prm)); cco_cleanup: puts("final combined"); @@ -103,7 +103,7 @@ int main(void) struct combined c = {.prm={.count=8}, .fib={14}}; int res; - cco_call_blocking(combined(&c), &res) { + cco_blocking_call(res = combined(&c)) { if (res == CCO_YIELD) printf("Prime(%d)=%lld, Fib(%d)=%lld\n", c.prm.idx, c.prm.result, diff --git a/misc/examples/coroutines/cotasks1.c b/misc/examples/coroutines/cotasks1.c index e4afbe2b..230bd62b 100644 --- a/misc/examples/coroutines/cotasks1.c +++ b/misc/examples/coroutines/cotasks1.c @@ -16,7 +16,7 @@ int next_value(struct next_value* co) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); + cco_await_timer(&co->tm, 1 + rand() % 2); co->val = rand(); cco_yield(); } @@ -88,7 +88,7 @@ int main(void) struct consume_items consume = {.n=5}; int count = 0; - cco_call_blocking(consume_items(&consume, &produce)) + cco_blocking_call(consume_items(&consume, &produce)) { ++count; //cco_sleep(0.001); diff --git a/misc/examples/coroutines/cotasks2.c b/misc/examples/coroutines/cotasks2.c index 4fdf98d9..d77a28bc 100644 --- a/misc/examples/coroutines/cotasks2.c +++ b/misc/examples/coroutines/cotasks2.c @@ -15,7 +15,7 @@ int next_value(struct next_value* co, cco_runtime* rt) { cco_routine (co) { while (true) { - cco_timer_await(&co->tm, 1 + rand() % 2); + cco_await_timer(&co->tm, 1 + rand() % 2); co->val = rand(); cco_yield(); } @@ -46,7 +46,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) while (true) { // await for next CCO_YIELD (or CCO_DONE) in next_value - cco_task_await(&p->next, rt, CCO_YIELD); + cco_await_task(&p->next, rt, CCO_YIELD); cstr_printf(&p->str, "item %d", p->next.val); print_time(); printf("produced %s\n", cstr_str(&p->str)); @@ -75,14 +75,14 @@ int consume_items(struct consume_items* c, cco_runtime* rt) for (c->i = 1; c->i <= c->n; ++c->i) { printf("consume #%d\n", c->i); - cco_task_await(&c->produce, rt, CCO_YIELD); + cco_await_task(&c->produce, rt, CCO_YIELD); print_time(); printf("consumed %s\n", cstr_str(&c->produce.str)); } cco_cleanup: cco_stop(&c->produce); - cco_task_resume(&c->produce, rt); + cco_resume_task(&c->produce, rt); puts("done consume"); } return 0; @@ -94,5 +94,5 @@ int main(void) .cco_func = consume_items, .n = 5, }; - cco_task_blocking(&consume); + cco_blocking_task(&consume); } diff --git a/misc/examples/coroutines/dining_philosophers.c b/misc/examples/coroutines/dining_philosophers.c index abe09204..e917c303 100644 --- a/misc/examples/coroutines/dining_philosophers.c +++ b/misc/examples/coroutines/dining_philosophers.c @@ -34,15 +34,15 @@ int philosopher(struct Philosopher* p) while (1) { duration = 1.0 + crandf()*2.0; printf("Philosopher %d is thinking for %.0f minutes...\n", p->id, duration*10); - cco_timer_await(&p->tm, duration); + cco_await_timer(&p->tm, duration); printf("Philosopher %d is hungry...\n", p->id); - cco_sem_await(p->left_fork); - cco_sem_await(p->right_fork); - + cco_await_sem(p->left_fork); + cco_await_sem(p->right_fork); + duration = 0.5 + crandf(); printf("Philosopher %d is eating for %.0f minutes...\n", p->id, duration*10); - cco_timer_await(&p->tm, duration); + cco_await_timer(&p->tm, duration); cco_sem_release(p->left_fork); cco_sem_release(p->right_fork); @@ -94,10 +94,10 @@ int main(void) cco_timer tm = cco_timer_from(15.0); // seconds csrand((uint64_t)time(NULL)); - while (!cco_done(&dine)) { + cco_blocking_call(dining(&dine)) + { if (cco_timer_expired(&tm)) cco_stop(&dine); - dining(&dine); // resume cco_sleep(0.001); ++n; } diff --git a/misc/examples/coroutines/filetask.c b/misc/examples/coroutines/filetask.c index bfce7810..28292801 100644 --- a/misc/examples/coroutines/filetask.c +++ b/misc/examples/coroutines/filetask.c @@ -21,7 +21,7 @@ int file_read(struct file_read* co, cco_runtime* rt) while (true) { // emulate async io: await 10ms per line - cco_timer_await(&co->tm, 0.003); + cco_await_timer(&co->tm, 0.003); if (!cstr_getline(&co->line, co->fp)) break; @@ -50,7 +50,7 @@ int count_line(struct count_line* co, cco_runtime* rt) while (true) { // await for next CCO_YIELD (or CCO_DONE) in file_read() - cco_task_await(&co->reader, rt, CCO_YIELD); + cco_await_task(&co->reader, rt, CCO_YIELD); if (rt->result == CCO_DONE) break; co->lineCount += 1; cco_yield(); @@ -73,7 +73,7 @@ int main(void) // Execute coroutine as top-level blocking int loop = 0; - cco_task_blocking(&countTask) { ++loop; } + cco_blocking_task(&countTask) { ++loop; } printf("line count = %d\n", countTask.lineCount); printf("exec count = %d\n", loop); diff --git a/misc/examples/coroutines/triples.c b/misc/examples/coroutines/triples.c index 9fd771ce..22914c2b 100644 --- a/misc/examples/coroutines/triples.c +++ b/misc/examples/coroutines/triples.c @@ -64,7 +64,7 @@ int main(void) struct triples t = {.max_c = 100}; int n = 0; - cco_call_blocking(triples_coro(&t)) { + cco_blocking_call(triples_coro(&t)) { if (gcd(t.a, t.b) > 1) continue; if (++n <= 20) -- cgit v1.2.3 From c27c266b6c4ae0e5e535b18c3790ee97416412b9 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Tue, 8 Aug 2023 12:28:15 +0200 Subject: Reverted cco_cleanup => cco_final. (cco_cleanup deprecated). Updated generator.c example. Misc internal refactoring. --- docs/coroutine_api.md | 12 +++++------ include/stc/cmap.h | 16 +++++++-------- include/stc/coroutine.h | 5 +++-- include/stc/forward.h | 6 ++---- include/stc/priv/cqueue_hdr.h | 4 ++-- misc/examples/coroutines/coread.c | 2 +- misc/examples/coroutines/coroutines.c | 6 +++--- misc/examples/coroutines/cotasks1.c | 4 ++-- misc/examples/coroutines/cotasks2.c | 4 ++-- misc/examples/coroutines/dining_philosophers.c | 4 ++-- misc/examples/coroutines/filetask.c | 4 ++-- misc/examples/coroutines/generator.c | 28 ++++++++++++++++++-------- misc/examples/coroutines/triples.c | 2 +- 13 files changed, 54 insertions(+), 43 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/docs/coroutine_api.md b/docs/coroutine_api.md index b356dcf0..6bd558f2 100644 --- a/docs/coroutine_api.md +++ b/docs/coroutine_api.md @@ -16,7 +16,7 @@ NB! ***cco_yield\*()*** / ***cco_await\*()*** may not be called from within a `s | | Function / operator | Description | |:----------|:-------------------------------------|:----------------------------------------| |`cco_result` | `CCO_DONE`, `CCO_AWAIT`, `CCO_YIELD` | Default set of return values from coroutines | -| | `cco_cleanup:` | Label for cleanup position in coroutine | +| | `cco_final:` | Label for cleanup position in coroutine | | `bool` | `cco_done(co)` | Is coroutine done? | | | `cco_routine(co) {}` | The coroutine scope | | | `cco_yield();` | Yield/suspend execution (return CCO_YIELD)| @@ -101,13 +101,13 @@ int triples(struct triples* i) { (int64_t)i->c * i->c) { if (i->c > i->max_c) - cco_return; // "jump" to cco_cleanup if defined, else exit scope. + cco_return; // "jump" to cco_final if defined, else exit scope. cco_yield(); } } } } - cco_cleanup: + cco_final: puts("done"); } return 0; // CCO_DONE @@ -160,7 +160,7 @@ int gcd1_triples(struct gcd1_triples* i) else cco_yield(); } - cco_cleanup: + cco_final: cco_stop(&i->tri); // to cleanup state if still active triples(&i->tri); // do cleanup (or no-op if done) } @@ -248,7 +248,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) printf("produced %s\n", cstr_str(&p->str)); cco_yield(); } - cco_cleanup: + cco_final: cstr_drop(&p->str); puts("done produce"); } @@ -273,7 +273,7 @@ int consume_items(struct consume_items* c, cco_runtime* rt) print_time(); printf("consumed %s\n", cstr_str(&c->produce.str)); } - cco_cleanup: + cco_final: cco_stop(&c->produce); cco_resume_task(&c->produce, rt); puts("done consume"); diff --git a/include/stc/cmap.h b/include/stc/cmap.h index cd7430ba..e0134964 100644 --- a/include/stc/cmap.h +++ b/include/stc/cmap.h @@ -306,7 +306,7 @@ STC_INLINE void _cx_MEMB(_wipe_)(_cx_Self* self) { if (self->size == 0) return; _cx_value* d = self->data, *_end = d + self->bucket_count; - chash_slot* s = self->slot; + struct chash_slot* s = self->slot; for (; d != _end; ++d) if ((s++)->hashx) _cx_MEMB(_value_drop)(d); @@ -321,7 +321,7 @@ STC_DEF void _cx_MEMB(_drop)(_cx_Self* self) { STC_DEF void _cx_MEMB(_clear)(_cx_Self* self) { _cx_MEMB(_wipe_)(self); self->size = 0; - c_memset(self->slot, 0, c_sizeof(chash_slot)*self->bucket_count); + c_memset(self->slot, 0, c_sizeof(struct chash_slot)*self->bucket_count); } #ifdef _i_ismap @@ -359,7 +359,7 @@ _cx_MEMB(_bucket_)(const _cx_Self* self, const _cx_keyraw* rkeyptr) { intptr_t _cap = self->bucket_count; intptr_t _idx = fastrange_2(_hash, _cap); _cx_result b = {NULL, true, (uint8_t)(_hash | 0x80)}; - const chash_slot* s = self->slot; + const struct chash_slot* s = self->slot; while (s[_idx].hashx) { if (s[_idx].hashx == b.hashx) { const _cx_keyraw _raw = i_keyto(_i_keyref(self->data + _idx)); @@ -394,8 +394,8 @@ _cx_MEMB(_clone)(_cx_Self m) { if (m.data) { _cx_value *d = (_cx_value *)i_malloc(c_sizeof(_cx_value)*m.bucket_count), *_dst = d, *_end = m.data + m.bucket_count; - const intptr_t _mem = c_sizeof(chash_slot)*(m.bucket_count + 1); - chash_slot *s = (chash_slot *)c_memcpy(i_malloc(_mem), m.slot, _mem); + const intptr_t _mem = c_sizeof(struct chash_slot)*(m.bucket_count + 1); + struct chash_slot *s = (struct chash_slot *)c_memcpy(i_malloc(_mem), m.slot, _mem); if (!(d && s)) { i_free(d), i_free(s), d = 0, s = 0, m.bucket_count = 0; } else @@ -417,14 +417,14 @@ _cx_MEMB(_reserve)(_cx_Self* self, const intptr_t _newcap) { _newbucks = cnextpow2(_newbucks); _cx_Self m = { (_cx_value *)i_malloc(_newbucks*c_sizeof(_cx_value)), - (chash_slot *)i_calloc(_newbucks + 1, sizeof(chash_slot)), + (struct chash_slot *)i_calloc(_newbucks + 1, sizeof(struct chash_slot)), self->size, _newbucks }; bool ok = m.data && m.slot; if (ok) { // Rehash: m.slot[_newbucks].hashx = 0xff; const _cx_value* d = self->data; - const chash_slot* s = self->slot; + const struct chash_slot* s = self->slot; for (intptr_t i = 0; i < _oldbucks; ++i, ++d) if ((s++)->hashx) { _cx_keyraw r = i_keyto(_i_keyref(d)); _cx_result b = _cx_MEMB(_bucket_)(&m, &r); @@ -441,7 +441,7 @@ _cx_MEMB(_reserve)(_cx_Self* self, const intptr_t _newcap) { STC_DEF void _cx_MEMB(_erase_entry)(_cx_Self* self, _cx_value* _val) { _cx_value* d = self->data; - chash_slot* s = self->slot; + struct chash_slot* s = self->slot; intptr_t i = _val - d, j = i, k; const intptr_t _cap = self->bucket_count; _cx_MEMB(_value_drop)(_val); diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index 0e592bae..cecd4002 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -38,7 +38,7 @@ int iterpair(struct iterpair* I) { for (I->y = 0; I->y < I->max_y; I->y++) cco_yield(); - cco_cleanup: // required if there is cleanup code + cco_final: // required if there is cleanup code puts("final"); } return 0; // CCO_DONE @@ -103,7 +103,8 @@ typedef enum { /* cco_blocking_call(): assumes coroutine returns a cco_result value (int) */ #define cco_blocking_call(corocall) while ((corocall) != CCO_DONE) -#define cco_cleanup \ +#define cco_cleanup cco_final // [deprecated] +#define cco_final \ *_state = CCO_STATE_CLEANUP; case CCO_STATE_CLEANUP #define cco_return \ diff --git a/include/stc/forward.h b/include/stc/forward.h index 085205cf..572a319f 100644 --- a/include/stc/forward.h +++ b/include/stc/forward.h @@ -107,8 +107,6 @@ typedef union { SELF##_node *last; \ } SELF -typedef struct chash_slot chash_slot; - #define _c_chash_types(SELF, KEY, VAL, MAP_ONLY, SET_ONLY) \ typedef KEY SELF##_key; \ typedef VAL SELF##_mapped; \ @@ -125,12 +123,12 @@ typedef struct chash_slot chash_slot; \ typedef struct { \ SELF##_value *ref, *_end; \ - chash_slot* sref; \ + struct chash_slot* sref; \ } SELF##_iter; \ \ typedef struct SELF { \ SELF##_value* data; \ - chash_slot* slot; \ + struct chash_slot* slot; \ intptr_t size, bucket_count; \ } SELF diff --git a/include/stc/priv/cqueue_hdr.h b/include/stc/priv/cqueue_hdr.h index 90539f36..1cad8684 100644 --- a/include/stc/priv/cqueue_hdr.h +++ b/include/stc/priv/cqueue_hdr.h @@ -96,8 +96,8 @@ STC_INLINE void _cx_MEMB(_copy)(_cx_Self* self, const _cx_Self* other) { STC_INLINE _cx_iter _cx_MEMB(_begin)(const _cx_Self* self) { return c_LITERAL(_cx_iter){ - _cx_MEMB(_empty)(self) ? NULL : self->data + self->start, - self->start, self + .ref=_cx_MEMB(_empty)(self) ? NULL : self->data + self->start, + .pos=self->start, ._s=self }; } diff --git a/misc/examples/coroutines/coread.c b/misc/examples/coroutines/coread.c index 359ca85d..6d3acdd7 100644 --- a/misc/examples/coroutines/coread.c +++ b/misc/examples/coroutines/coread.c @@ -21,7 +21,7 @@ int file_read(struct file_read* g) cco_await(!cstr_getline(&g->line, g->fp)); - cco_cleanup: + cco_final: printf("finish\n"); cstr_drop(&g->line); if (g->fp) fclose(g->fp); diff --git a/misc/examples/coroutines/coroutines.c b/misc/examples/coroutines/coroutines.c index faeb71f6..802a976a 100644 --- a/misc/examples/coroutines/coroutines.c +++ b/misc/examples/coroutines/coroutines.c @@ -34,7 +34,7 @@ int prime(struct prime* g) { cco_yield(); } } - cco_cleanup: + cco_final: printf("final prm\n"); } return 0; @@ -68,7 +68,7 @@ int fibonacci(struct fibonacci* g) { } cco_yield(); } - cco_cleanup: + cco_final: printf("final fib\n"); } return 0; @@ -92,7 +92,7 @@ int combined(struct combined* g) { cco_reset(&g->prm); cco_await_call(prime(&g->prm)); - cco_cleanup: + cco_final: puts("final combined"); } return 0; diff --git a/misc/examples/coroutines/cotasks1.c b/misc/examples/coroutines/cotasks1.c index 230bd62b..7df4eb34 100644 --- a/misc/examples/coroutines/cotasks1.c +++ b/misc/examples/coroutines/cotasks1.c @@ -52,7 +52,7 @@ int produce_items(struct produce_items* p) printf("produced %s\n", cstr_str(&p->str)); cco_yield(); } - cco_cleanup: + cco_final: cstr_drop(&p->str); puts("done produce"); } @@ -76,7 +76,7 @@ int consume_items(struct consume_items* c, struct produce_items* p) print_time(); printf("consumed %s\n", cstr_str(&p->str)); } - cco_cleanup: + cco_final: puts("done consume"); } return 0; diff --git a/misc/examples/coroutines/cotasks2.c b/misc/examples/coroutines/cotasks2.c index d77a28bc..f6257a7e 100644 --- a/misc/examples/coroutines/cotasks2.c +++ b/misc/examples/coroutines/cotasks2.c @@ -53,7 +53,7 @@ int produce_items(struct produce_items* p, cco_runtime* rt) cco_yield(); } - cco_cleanup: + cco_final: cstr_drop(&p->str); puts("done produce"); } @@ -80,7 +80,7 @@ int consume_items(struct consume_items* c, cco_runtime* rt) printf("consumed %s\n", cstr_str(&c->produce.str)); } - cco_cleanup: + cco_final: cco_stop(&c->produce); cco_resume_task(&c->produce, rt); puts("done consume"); diff --git a/misc/examples/coroutines/dining_philosophers.c b/misc/examples/coroutines/dining_philosophers.c index e917c303..d353b3b9 100644 --- a/misc/examples/coroutines/dining_philosophers.c +++ b/misc/examples/coroutines/dining_philosophers.c @@ -48,7 +48,7 @@ int philosopher(struct Philosopher* p) cco_sem_release(p->right_fork); } - cco_cleanup: + cco_final: printf("Philosopher %d finished\n", p->id); } return 0; @@ -76,7 +76,7 @@ int dining(struct Dining* d) cco_yield(); // suspend, return control back to main } - cco_cleanup: + cco_final: for (int i = 0; i < num_philosophers; ++i) { cco_stop(&d->ph[i]); philosopher(&d->ph[i]); diff --git a/misc/examples/coroutines/filetask.c b/misc/examples/coroutines/filetask.c index 0607442d..74388359 100644 --- a/misc/examples/coroutines/filetask.c +++ b/misc/examples/coroutines/filetask.c @@ -28,7 +28,7 @@ int file_read(struct file_read* co, cco_runtime* rt) cco_yield(); } - cco_cleanup: + cco_final: fclose(co->fp); cstr_drop(&co->line); puts("done file_read"); @@ -56,7 +56,7 @@ int count_line(struct count_line* co, cco_runtime* rt) cco_yield(); } - cco_cleanup: + cco_final: cstr_drop(&co->path); puts("done count_line"); } diff --git a/misc/examples/coroutines/generator.c b/misc/examples/coroutines/generator.c index 3f51ce9c..96498498 100644 --- a/misc/examples/coroutines/generator.c +++ b/misc/examples/coroutines/generator.c @@ -2,12 +2,15 @@ #include #include +#include typedef struct { - int size; + int max_triples; int a, b, c; } Triple; +// Create an iterable generator over Triple with count items. +// Requires coroutine Triple_next() and function Triple_begin() to be defined. cco_iter_struct(Triple, int count; ); @@ -20,16 +23,15 @@ int Triple_next(Triple_iter* it) { for (g->a = 1; g->a < g->c; ++g->a) { for (g->b = g->a; g->b < g->c; ++g->b) { if (g->a*g->a + g->b*g->b == g->c*g->c) { - if (it->count++ == g->size) + if (it->count++ == g->max_triples) cco_return; cco_yield(); } } } } - cco_cleanup: - it->ref = NULL; - puts("done"); + cco_final: + it->ref = NULL; // stop the iterator } return 0; } @@ -43,12 +45,22 @@ Triple_iter Triple_begin(Triple* g) { int main(void) { - puts("Pythagorean triples; stops at 100 triples or c >= 100:"); - Triple triple = {.size=100}; + puts("Pythagorean triples.\nGet max 200 triples with c < 50:"); + Triple triple = {.max_triples=200}; + c_foreach (i, Triple, triple) { - if (i.ref->c < 100) + if (i.ref->c < 50) printf("%u: (%d, %d, %d)\n", i.count, i.ref->a, i.ref->b, i.ref->c); else cco_stop(&i); } + + puts("\nGet the 10 first triples with odd a's and a <= 20:"); + c_forfilter (i, Triple, triple, + i.ref->a <= 20 && + (i.ref->a & 1) && + c_flt_take(i, 10) + ){ + printf("%d: (%d, %d, %d)\n", c_flt_getcount(i), i.ref->a, i.ref->b, i.ref->c); + } } diff --git a/misc/examples/coroutines/triples.c b/misc/examples/coroutines/triples.c index 22914c2b..d6ce2791 100644 --- a/misc/examples/coroutines/triples.c +++ b/misc/examples/coroutines/triples.c @@ -40,7 +40,7 @@ int triples_coro(struct triples* t) { } } } - cco_cleanup: + cco_final: puts("done"); } return 0; -- cgit v1.2.3 From 0c29a8413619870f23682b74c032137e81db17e2 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Fri, 11 Aug 2023 16:04:52 +0200 Subject: Minor internals. --- include/stc/coroutine.h | 21 ++++++++++++--------- include/stc/priv/template.h | 24 ++++++++---------------- 2 files changed, 20 insertions(+), 25 deletions(-) (limited to 'include/stc/coroutine.h') diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h index cecd4002..5f06e7b8 100644 --- a/include/stc/coroutine.h +++ b/include/stc/coroutine.h @@ -131,16 +131,19 @@ typedef enum { (void)((co)->cco_state = 0) /* - * Generators + * Iterator (for generators) + * User define: Gen must be an existing typedef struct: + * Gen_iter Gen_begin(Gen* g); // function + * int Gen_next(Gen_iter* it); // coroutine */ -#define cco_iter_struct(Name, ...) \ - typedef Name Name##_value; \ - typedef struct { \ - Name##_value* ref; \ +#define cco_iter_struct(Gen, ...) \ + typedef Gen Gen##_value; \ + typedef struct Gen##_iter { \ + Gen##_value* ref; \ int cco_state; \ __VA_ARGS__ \ - } Name##_iter + } Gen##_iter /* * Tasks @@ -148,9 +151,9 @@ typedef enum { struct cco_runtime; -#define cco_task_struct(Name, ...) \ - struct Name { \ - int (*cco_func)(struct Name*, struct cco_runtime*); \ +#define cco_task_struct(Task, ...) \ + struct Task { \ + int (*cco_func)(struct Task*, struct cco_runtime*); \ int cco_state, cco_expect; \ __VA_ARGS__ \ } diff --git a/include/stc/priv/template.h b/include/stc/priv/template.h index 3b8a5b39..65dee203 100644 --- a/include/stc/priv/template.h +++ b/include/stc/priv/template.h @@ -140,14 +140,14 @@ #ifndef i_keyclone #define i_keyclone c_PASTE(i_key, _clone) #endif - #if !defined i_keyto && defined i_keyraw - #define i_keyto c_PASTE(i_key, _toraw) + #ifndef i_keydrop + #define i_keydrop c_PASTE(i_key, _drop) #endif #if !defined i_keyfrom && defined i_keyraw #define i_keyfrom c_PASTE(i_key, _from) #endif - #ifndef i_keydrop - #define i_keydrop c_PASTE(i_key, _drop) + #if !defined i_keyto && defined i_keyraw + #define i_keyto c_PASTE(i_key, _toraw) #endif #if !defined i_keyraw && (defined i_cmp || defined i_less || defined i_eq || defined i_hash) #define i_use_cmp @@ -245,22 +245,14 @@ #ifndef i_valclone #define i_valclone c_PASTE(i_val, _clone) #endif - #if !defined i_valto && defined i_valraw - #define i_valto c_PASTE(i_val, _toraw) + #ifndef i_valdrop + #define i_valdrop c_PASTE(i_val, _drop) #endif #if !defined i_valfrom && defined i_valraw #define i_valfrom c_PASTE(i_val, _from) #endif - #ifndef i_valdrop - #define i_valdrop c_PASTE(i_val, _drop) - #endif -#endif - -#if !defined i_valraw && !defined i_no_clone - #if !defined i_valfrom && defined i_valclone - #define i_valfrom i_valclone - #elif !defined i_valclone && defined i_valfrom - #define i_valclone i_valfrom + #if !defined i_valto && defined i_valraw + #define i_valto c_PASTE(i_val, _toraw) #endif #endif -- cgit v1.2.3