├── CMakeLists.txt ├── README.md ├── premake4.lua ├── tinycoroutine.c ├── tinycoroutine.h └── tinycoroutine_test.c /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(tinycoroutine) 2 | 3 | cmake_minimum_required (VERSION 3.0.2) 4 | 5 | add_library(tinycoroutine STATIC "tinycoroutine.c") 6 | 7 | set_target_properties(tinycoroutine PROPERTIES PUBLIC_HEADER "tinycoroutine.h") 8 | 9 | install(TARGETS tinycoroutine 10 | LIBRARY DESTINATION lib 11 | ARCHIVE DESTINATION lib 12 | PUBLIC_HEADER DESTINATION include) 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TinyCoroutine 2 | ============= 3 | 4 | TinyCoroutine is a simple coroutine library. It currently runs on x86 and ARM and should be easy to extend to other platforms. TinyCoroutine uses setjmp()/longjmp() and a small bit of ASM to handle execution context switches. 5 | 6 | TinyCoroutine provides a high-level and low level API. The high level API provides a coroutine system which manages multiple coroutine contexts and allows easy switching between each context. The low level API provides a ucontext-like interface which allows one to obtain a state of execution and switch between execution states. 7 | 8 | High Level API 9 | -------------- 10 | 11 | void tinyco_init(struct tinyco_t *context,tinyco_alloc_func_t alloc,tinyco_free_func_t release); 12 | 13 | Initializes tinycoroutine context. 14 | 15 | void tinyco_spawn(struct tinyco_t *context,tinyco_func_t entry,void *param,void *stack,size_t stack_size); 16 | 17 | Schedules the function entry(param) with stack to be executed. 18 | 19 | void tinyco_exec(struct tinyco_t *context); 20 | 21 | Switches to the next coroutine and returns only when all coroutines have completed. This is equaivalent to calling tinyco_yield() in an loop. 22 | 23 | int tinyco_yield(struct tinyco_t *context); 24 | 25 | Saves the current execution and switches to the next coroutine. 26 | 27 | void tinyco_exit(struct tinyco_t *context,int exitCode); 28 | 29 | Terminates the current coroutine. 30 | 31 | Low Level API 32 | ------------- 33 | 34 | void tinyco_context_create(struct tinyco_context_t *coro,tinyco_func_t entry,void *param,void *stack,size_t stack_size,struct tinyco_context_t *ret); 35 | 36 | Initializes a execution context with execution starting at function entry(param) and stack. 37 | 38 | void tinyco_context_get(struct tinyco_context_t *coro); 39 | 40 | Initializes a execution context saving the current execution state. 41 | 42 | void tinyco_context_swap(struct tinyco_context_t *coro,struct tinyco_context_t *prev); 43 | 44 | Switches to the execution context coro while saving the current context to prev. 45 | -------------------------------------------------------------------------------- /premake4.lua: -------------------------------------------------------------------------------- 1 | solution "TinyCoroutine" 2 | 3 | configurations { "Debug", "Release" } 4 | 5 | configuration "Debug" 6 | flags { "Symbols" } 7 | configuration "Release" 8 | flags { "Optimize" } 9 | defines { "NDEBUG" } 10 | 11 | project "tinycoroutine" 12 | language "C" 13 | kind "StaticLib" 14 | 15 | files { 16 | "tinycoroutine.c", 17 | "tinycoroutine.h", 18 | } 19 | 20 | project "coroutine_test" 21 | language "C" 22 | kind "ConsoleApp" 23 | 24 | files { 25 | "tinycoroutine_test.c" 26 | } 27 | 28 | links { "tinycoroutine" } 29 | -------------------------------------------------------------------------------- /tinycoroutine.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Sound 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | // On gcc Release, fortify is enabled, and longjmp checks the returning stack is 18 | // valid by comparing the return stack address. But based on how coroutine works 19 | // with alternate stacks, this check incorrectly gets tripped. Disable fortify 20 | // for this file. 21 | 22 | #undef _FORTIFY_SOURCE 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "tinycoroutine.h" 31 | 32 | #ifdef __GNUC__ 33 | #define DEF_FUNC_NO_INLINE(ret,name,params) ret __attribute__ ((noinline)) name params 34 | #elif _MSC_VER 35 | #define DEF_FUNC_NO_INLINE(ret,name,params) __declspec(noinline) ret name params 36 | #else 37 | #define DEF_FUNC_NO_INLINE(ret,name,params) ret name params 38 | #endif 39 | 40 | #if defined(__APPLE__) || defined(__linux__) 41 | #define setjmp _setjmp 42 | #define longjmp _longjmp 43 | #endif 44 | 45 | void tinyco_init(struct tinyco_t *context,tinyco_alloc_func_t alloc,tinyco_free_func_t release) 46 | { 47 | context->context_count = 0; 48 | context->context_root.next = &context->context_root; 49 | context->context_root.prev = &context->context_root; 50 | context->current_context = &context->context_root; 51 | context->alloc = alloc ? alloc : malloc; 52 | context->release = release ? release : free; 53 | } 54 | 55 | struct tinyco_spawn_data_t 56 | { 57 | struct tinyco_t *context; 58 | tinyco_func_t entry; 59 | void *param; 60 | struct tinyco_context_list_t *context_list; 61 | struct tinyco_context_t ret_context; 62 | }; 63 | 64 | static void tinyco_spawn_func(struct tinyco_spawn_data_t *data) 65 | { 66 | // save a copy of tinyco_spawn_data_t to stack variables 67 | struct tinyco_t *context = data->context; 68 | tinyco_func_t entry = data->entry; 69 | void *param = data->param; 70 | 71 | // return to the tinyco_spawn() function 72 | tinyco_context_swap(&data->ret_context,&data->context_list->context); 73 | 74 | // data is invalid at this point 75 | 76 | // enter the function 77 | entry(param); 78 | 79 | tinyco_exit(context,0); 80 | } 81 | 82 | void tinyco_spawn(struct tinyco_t *context,tinyco_func_t entry,void *param,void *stack,size_t stack_size) 83 | { 84 | struct tinyco_context_list_t *ctx = (struct tinyco_context_list_t *)context->alloc(sizeof(struct tinyco_context_list_t)); 85 | struct tinyco_spawn_data_t data; 86 | 87 | data.context = context; 88 | data.entry = entry; 89 | data.param = param; 90 | data.context_list = ctx; 91 | 92 | tinyco_context_create(&ctx->context,(tinyco_func_t)tinyco_spawn_func,&data,stack,stack_size,NULL); 93 | 94 | // swap into the new context temporarily so we can save tinyco_spawn_data_t into stack variables 95 | tinyco_context_swap(&ctx->context, &data.ret_context); 96 | 97 | // add to list 98 | ctx->next = &context->context_root; 99 | ctx->prev = context->context_root.prev; 100 | context->context_root.prev->next = ctx; 101 | context->context_root.prev = ctx; 102 | 103 | context->context_count++; 104 | } 105 | 106 | void tinyco_exec(struct tinyco_t *context) 107 | { 108 | while(tinyco_yield(context) != 0); 109 | } 110 | 111 | int tinyco_yield(struct tinyco_t *context) 112 | { 113 | struct tinyco_context_list_t *prev_context = context->current_context; 114 | context->current_context = context->current_context->next; 115 | 116 | tinyco_context_swap(&context->current_context->context,&prev_context->context); 117 | 118 | return context->context_count; 119 | } 120 | 121 | void tinyco_exit(struct tinyco_t *context,int exitCode) 122 | { 123 | struct tinyco_context_list_t *next; 124 | 125 | // unlink list 126 | context->current_context->prev->next = context->current_context->next; 127 | context->current_context->next->prev = context->current_context->prev; 128 | 129 | next = context->current_context->next; 130 | 131 | context->release(context->current_context); 132 | context->context_count--; 133 | 134 | context->current_context = next; 135 | tinyco_context_swap(&next->context,NULL); 136 | } 137 | 138 | DEF_FUNC_NO_INLINE(static void, tinyco_entry, (struct tinyco_context_t * volatile coro)) 139 | { 140 | jmp_buf ret; 141 | memcpy(ret, coro->ctxt, sizeof(jmp_buf)); 142 | 143 | /* Save the entry context, return to caller */ 144 | if (setjmp(coro->ctxt) == 0) { 145 | JMPBUF_DISABLE_SEH(coro->ctxt); 146 | longjmp(ret, 1); 147 | } 148 | 149 | coro->entry(coro->param); 150 | 151 | if (coro->ret) tinyco_context_swap(coro->ret, NULL); 152 | 153 | exit(1); 154 | } 155 | 156 | DEF_FUNC_NO_INLINE(static int,tinyco_get_stack_ptr_in_jmp_buf, (jmp_buf *lo_buf)) 157 | { 158 | size_t local = (size_t)&local; 159 | size_t n; 160 | jmp_buf hi_buf; 161 | volatile size_t *lo_ptr, *hi_ptr; 162 | int i; 163 | 164 | setjmp(hi_buf); 165 | 166 | /* attempt to determine which offset in the jmp_buf stores the stack pointer */ 167 | n = sizeof(jmp_buf) / sizeof(size_t); 168 | lo_ptr = (size_t *)*lo_buf; 169 | hi_ptr = (size_t *)hi_buf; 170 | for(i = 0; i < n; ++i) { 171 | if(hi_ptr[i] <= local && local <= lo_ptr[i] && (lo_ptr[i] - hi_ptr[i]) < 1024) { 172 | return i; 173 | } 174 | } 175 | 176 | return -1; 177 | } 178 | 179 | DEF_FUNC_NO_INLINE(static int,tinyco_get_stack_dir2, (volatile int *x) ) 180 | { 181 | volatile int y; 182 | return &y < x ? -1 : 1; 183 | } 184 | 185 | static int tinyco_get_stack_dir() 186 | { 187 | volatile int x; 188 | return tinyco_get_stack_dir2(&x); 189 | } 190 | 191 | /* 192 | * This is kinda a ridiculous hack.. but on x64 on first 4 params passed by registers. 193 | * The rest passed on stack. We can use the jmp_buf hack on msvc x64 to call the entry function. 194 | */ 195 | #if defined(_MSC_VER) && defined(_WIN64) 196 | static DEF_FUNC_NO_INLINE(void, tinyco_msvc_x64_entry, (__int64 dummy1,__int64 dummy2,__int64 dummy3,__int64 dummy4,void (*entry)(struct tinyco_context_t *),struct tinyco_context_t *coro) ) 197 | { 198 | entry(coro); 199 | } 200 | 201 | #endif 202 | 203 | /* 204 | * This is the non-portable bit which requires a bit of ASM. 205 | * The stack pointer should be replaced with the passed stack pointer and the entry function should be 206 | * called with the new stack in use. 207 | */ 208 | static void tinyco_swap_stack_and_call(void *stack,void (*entry)(struct tinyco_context_t *),struct tinyco_context_t *coro) 209 | { 210 | #if defined(_MSC_VER) && defined(_WIN32) && !defined(_WIN64) 211 | __asm { 212 | mov ecx, stack 213 | lea esp, [ecx - 4] 214 | and esp, -16 215 | mov eax, coro 216 | mov [esp], eax 217 | call entry 218 | } 219 | #elif defined(_MSC_VER) && defined(_WIN64) 220 | jmp_buf buf; 221 | _JUMP_BUFFER *jb; 222 | 223 | /* on x86 windows, we can't use inline asm, so rely on 224 | * hacking the jmp_buf to exchange the stack and call our entry func */ 225 | if(setjmp(buf) == 0) { 226 | jb = (_JUMP_BUFFER *)buf; 227 | JMPBUF_DISABLE_SEH(buf); 228 | jb->Rsp = (((unsigned __int64)stack - 6 * sizeof(__int64)) & ~0xFLL) - sizeof(__int64); // align 16-byte, sub 8 for return addr 229 | *((unsigned __int64 *)jb->Rsp) = 0xCCCCCCCCCCCCCCCCLL; // return address, but we'll never reach this 230 | *((unsigned __int64 *)jb->Rsp + 5) = (unsigned __int64)entry; // param 5 231 | *((unsigned __int64 *)jb->Rsp + 6) = (unsigned __int64)coro; // param 6 232 | jb->Rip = (unsigned __int64)tinyco_msvc_x64_entry; 233 | longjmp(buf,1); 234 | } 235 | #elif defined(__GNUC__) && defined(__x86_64__) 236 | /* x64 GNU C */ 237 | __asm__ __volatile__ ( 238 | "movq %0, %%rsp\n" /* replace stack pointer */ 239 | "andq $-16, %%rsp\n" /* align 16-bytes */ 240 | "callq *%2\n" /* call function */ 241 | : 242 | : "g" (stack), 243 | "D" (coro), /* load coro into edi for the first parameter to pass */ 244 | "r" (entry), 245 | "c" (coro) /* make windows happy */ 246 | : ); 247 | #elif defined(__GNUC__) && defined(__i386__) 248 | __asm__ __volatile__ ( 249 | "leal -4(%0), %%esp\n" /* replace stack pointer */ 250 | "andl $-16, %%esp\n" /* align 16-bytes */ 251 | "movl %1, (%%esp)\n" /* Pass 1 parameter */ 252 | "calll *%2\n" /* call function */ 253 | : 254 | : "r" (stack), "r" (coro), "r" (entry) 255 | : ); 256 | #elif defined(__GNUC__) && defined(__arm__) 257 | __asm__ __volatile__ ( 258 | "mov sp, %0\n" 259 | "mov r0, %1\n" 260 | "bx %2\n" 261 | : 262 | : "r" (stack), "r" (coro), "r" (entry) 263 | : "r0"); 264 | #else 265 | 266 | /* This is a very flaky ANSI hack to attempt to swap the stack and call 267 | * the entry function. Try not to use this if possible. Uncomment below 268 | * if you want to use regardless. */ 269 | 270 | #error Not supported for your compiler / platform. 271 | 272 | static struct tinycoroutine_t *c; 273 | jmp_buf buf; 274 | 275 | c = coro; 276 | 277 | /* 278 | * This fallback routine attempts to set the stack by using the auto-detected 279 | * jmp_buf offset. It uses setjmp to get the existing jmp_buf, then hacks 280 | * in the stack address into the saved jmp_buf. When we longjmp with the hacked 281 | * jmp_buf, the stack will be overloaded. 282 | */ 283 | 284 | // swap the stack pointer 285 | if(setjmp(buf) == 0) { 286 | ((size_t *)buf)[tinyco_get_stack_ptr_in_jmp_buf(&buf)] = stack; 287 | longjmp(buf,1); 288 | } 289 | 290 | /* registers are messed at this point, don't insert code here */ 291 | 292 | /* static function call to coroutine_entry 293 | this will enter the coroutine in a state with the new stack */ 294 | tinyco_entry(c); 295 | #endif 296 | 297 | assert(0 && "Should not return here."); 298 | } 299 | 300 | 301 | void tinyco_context_create(struct tinyco_context_t *coro,tinyco_func_t entry,void *param,void *stack,size_t stack_size,struct tinyco_context_t *ret) 302 | { 303 | coro->entry = entry; 304 | coro->param = param; 305 | coro->ret = ret; 306 | 307 | if(setjmp(coro->ctxt) == 0) { 308 | JMPBUF_DISABLE_SEH(coro->ctxt); 309 | char *stackBase = tinyco_get_stack_dir() < 0 ? (char *)stack + stack_size : (char *)stack; 310 | tinyco_swap_stack_and_call(stackBase,tinyco_entry,coro); 311 | /* not expected to return here */ 312 | } 313 | } 314 | 315 | void tinyco_context_swap(struct tinyco_context_t * volatile coro, struct tinyco_context_t * volatile prev) 316 | { 317 | if(!prev || setjmp(prev->ctxt) == 0) { 318 | if (prev) JMPBUF_DISABLE_SEH(prev->ctxt); 319 | longjmp(coro->ctxt,1); 320 | } 321 | } 322 | 323 | #ifdef setjmp 324 | #undef setjmp 325 | #endif 326 | 327 | #ifdef longjmp 328 | #undef longjmp 329 | #endif 330 | 331 | #undef DEF_FUNC_NO_INLINE 332 | 333 | -------------------------------------------------------------------------------- /tinycoroutine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Sound 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef INC_COROUTINE_H 17 | #define INC_COROUTINE_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #if defined(_MSC_VER) && defined(_WIN64) 26 | #define JMPBUF_DISABLE_SEH(jb) ((_JUMP_BUFFER *)jb)->Frame = 0 27 | #else 28 | #define JMPBUF_DISABLE_SEH(jb) ((void)0) 29 | #endif 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef void (*tinyco_func_t)(void *); 36 | typedef void *(*tinyco_alloc_func_t)(size_t); 37 | typedef void (*tinyco_free_func_t)(void *); 38 | 39 | struct tinyco_context_t 40 | { 41 | jmp_buf ctxt; 42 | tinyco_func_t entry; 43 | void *param; 44 | struct tinyco_context_t *ret; 45 | }; 46 | 47 | struct tinyco_context_list_t 48 | { 49 | struct tinyco_context_t context; 50 | struct tinyco_context_list_t *prev, *next; 51 | }; 52 | 53 | struct tinyco_t 54 | { 55 | tinyco_alloc_func_t alloc; 56 | tinyco_free_func_t release; 57 | struct tinyco_context_list_t context_root; 58 | struct tinyco_context_list_t *current_context; 59 | int context_count; 60 | }; 61 | 62 | 63 | /******************************************************************************************************************************************************** 64 | * HI-LEVEL API 65 | * The high level APIs provides a system which maintains a running 66 | * list of coroutines that can be easily switched between. 67 | ********************************************************************************************************************************************************/ 68 | 69 | /** 70 | * Creates a execution context which manages a list of running coroutines. 71 | * @param[out] context Pointer to an uninitialized @a tinyco_t struct 72 | * @param alloc Optional allocation function. 73 | * @param release Optional release function. 74 | */ 75 | void tinyco_init(struct tinyco_t *context,tinyco_alloc_func_t alloc,tinyco_free_func_t release); 76 | 77 | /** 78 | * Schedules a new coroutine on the current context calling entry(param) as the entry function with stack of @a stack_size. 79 | * @param context tinyco_t context 80 | * @param entry Entry function 81 | * @param param Parameter 82 | * @param stack Pointer to stack pointer 83 | * @param stack_size Stack size 84 | */ 85 | void tinyco_spawn(struct tinyco_t *context,tinyco_func_t entry,void *param,void *stack,size_t stack_size); 86 | 87 | /** 88 | * Starts the coroutine execution loop, executing each coroutine in round robin style. 89 | * This function does not return until all coroutines have completed. 90 | * @param context tinyco_t context 91 | */ 92 | void tinyco_exec(struct tinyco_t *context); 93 | 94 | /** 95 | * Saves the current execution state to the current coroutine and switches to the next available coroutine. 96 | * @param context tinyco_t context 97 | * @return Returns the number of running coroutines. 98 | */ 99 | int tinyco_yield(struct tinyco_t *context); 100 | 101 | /** 102 | * Terminates the current coroutine and removes it from execution. 103 | * This routine terminates the current execution and does not perform any cleanup, 104 | * it is up to the user to release unused resources before calling @a tinyco_exit. 105 | * @param context tinyco_t context 106 | * @param exitCode Exit code (currently does nothing). 107 | */ 108 | void tinyco_exit(struct tinyco_t *context,int exitCode); 109 | 110 | 111 | /******************************************************************************************************************************************************** 112 | * LOW-LEVEL API 113 | * The level level APIs provides a low level ucontext-like interface to coroutines. 114 | * The low level apis allows one to obtain a state of executation and switch between 115 | * them. 116 | ********************************************************************************************************************************************************/ 117 | 118 | 119 | /** 120 | * Initializes a coroutine context, with an entry function and a stack. 121 | * When @a coroutine_swap is called with the context, execution begins at the 122 | * entry function and once the entry function has completed, the coroutine passed 123 | * in @a ret is switched to or the routine exists with a @a exit() call. 124 | * 125 | * @param coro Address of an uninitialized coroutine_t structure. 126 | * @param entry Entry function of the coroutine. 127 | * @param param User passed pointer which is passed as the first paramter in the entry function. 128 | * @param stack Pointer to the buffer to be used for the stack 129 | * @param stack_size Size of the stack buffer. 130 | * @param ret Pointer to an initialized coroutine_t to switch to for when the coroutine returns. 131 | * NULL may be passed here, which will cause the coroutine to call @a exit() when it completes. 132 | */ 133 | void tinyco_context_create(struct tinyco_context_t *coro,tinyco_func_t entry,void *param,void *stack,size_t stack_size,struct tinyco_context_t *ret); 134 | 135 | /** 136 | * Initializes a coroutine context with the current state of execution. 137 | * 138 | * @param coro Address of an uninitialized coroutine_t structure. 139 | */ 140 | // void tinyco_context_get(struct tinyco_context_t *coro); 141 | #define tinyco_context_get(coro) \ 142 | do { \ 143 | if(setjmp((coro)->ctxt) == 0) { \ 144 | JMPBUF_DISABLE_SEH((coro)->ctxt); \ 145 | (coro)->entry = NULL; \ 146 | } \ 147 | } while (0) 148 | 149 | /** 150 | * Saves the current coroutine context and switches to the passed coroutine. 151 | * @param coro Coroutine context to switch to 152 | * @param prev Current coroutine context is saved. This parameter can be NULL. 153 | */ 154 | void tinyco_context_swap(struct tinyco_context_t * volatile coro, struct tinyco_context_t * volatile prev); 155 | 156 | #ifdef __cplusplus 157 | } 158 | #endif 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /tinycoroutine_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Sound 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | * PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include 17 | #include "tinycoroutine.h" 18 | 19 | struct tinyco_t co; 20 | struct tinyco_context_t c1, c2; 21 | char stack1[32*1024]; 22 | char stack2[32*1024]; 23 | char stack3[32*1024]; 24 | 25 | void funcA(void *p) 26 | { 27 | int i; 28 | for(i = 10; i < 20; ++i) { 29 | printf("A%d\n",i); 30 | tinyco_context_swap(&c2,&c1); 31 | } 32 | } 33 | 34 | void funcB(void *p) 35 | { 36 | int i; 37 | for(i = 0; i < 10; ++i) { 38 | printf("B%d\n",i); 39 | tinyco_context_swap(&c1,&c2); 40 | } 41 | 42 | } 43 | 44 | void funcC(void *p) 45 | { 46 | int i; 47 | for(i = 0; i < 8; ++i) { 48 | printf("C%d\n",i); 49 | tinyco_yield(&co); 50 | } 51 | tinyco_exit(&co, 0); 52 | } 53 | 54 | void funcD(void *p) 55 | { 56 | int i; 57 | for(i = 0; i < 5; ++i) { 58 | printf("D%d\n",i); 59 | tinyco_yield(&co); 60 | } 61 | } 62 | 63 | void funcE(void *p) 64 | { 65 | int i; 66 | for(i = 0; i < 10; ++i) { 67 | printf("E%d\n",i); 68 | tinyco_yield(&co); 69 | } 70 | } 71 | 72 | int main(int argc,char *argv[]) 73 | { 74 | struct tinyco_context_t cur; 75 | 76 | tinyco_context_get(&cur); 77 | tinyco_context_create(&c1,funcA,NULL,stack1,sizeof(stack1),&cur); 78 | tinyco_context_create(&c2,funcB,NULL,stack2,sizeof(stack2),&cur); 79 | 80 | tinyco_context_swap(&c1,&cur); 81 | 82 | 83 | tinyco_init(&co, NULL, NULL); 84 | tinyco_spawn(&co, funcC, NULL, stack1, sizeof(stack1)); 85 | tinyco_spawn(&co, funcD, NULL, stack2, sizeof(stack2)); 86 | tinyco_spawn(&co, funcE, NULL, stack3, sizeof(stack3)); 87 | 88 | tinyco_exec(&co); 89 | 90 | return 0; 91 | } 92 | --------------------------------------------------------------------------------