diff options
| author | _Tradam <[email protected]> | 2023-09-08 01:29:47 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-09-08 01:29:47 +0000 |
| commit | 3c76c7f3d5db3f9586a90d03f8fbb02d79de9acd (patch) | |
| tree | afbe4b540967223911f7c5de36559b82154f02f3 /include/stc/coroutine.h | |
| parent | 0841165881871ee01b782129be681209aeed2423 (diff) | |
| parent | 1a72205fe05c2375cfd380dd8381a8460d9ed8d1 (diff) | |
| download | STC-modified-modified.tar.gz STC-modified-modified.zip | |
Diffstat (limited to 'include/stc/coroutine.h')
| -rw-r--r-- | include/stc/coroutine.h | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/include/stc/coroutine.h b/include/stc/coroutine.h new file mode 100644 index 00000000..5f06e7b8 --- /dev/null +++ b/include/stc/coroutine.h @@ -0,0 +1,288 @@ +/* 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 <stdio.h> +#include <stc/coroutine.h> + +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_final: // 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_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_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_blocking_call(): assumes coroutine returns a cco_result value (int) */ +#define cco_blocking_call(corocall) while ((corocall) != CCO_DONE) + +#define cco_cleanup cco_final // [deprecated] +#define cco_final \ + *_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) + +/* + * 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(Gen, ...) \ + typedef Gen Gen##_value; \ + typedef struct Gen##_iter { \ + Gen##_value* ref; \ + int cco_state; \ + __VA_ARGS__ \ + } Gen##_iter + +/* + * Tasks + */ + +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__ \ + } + +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(task, rt) \ + (task)->cco_func(task, rt) + +#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_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_resume_task(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_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; \ + } 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 <sys/time.h> + 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_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); \ + } 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 |
