├── LICENSE ├── Makefile ├── README.md ├── coroutine.c ├── coroutine.h └── main.c /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 codingnow.com Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all : main 2 | 3 | main : main.c coroutine.c 4 | gcc -g -Wall -o $@ $^ 5 | 6 | clean : 7 | rm main 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | It's an asymmetric coroutine library (like lua). 2 | 3 | You can use coroutine_open to open a schedule first, and then create coroutine in that schedule. 4 | 5 | You should call coroutine_resume in the thread that you call coroutine_open, and you can't call it in a coroutine in the same schedule. 6 | 7 | Coroutines in the same schedule share the stack , so you can create many coroutines without worry about memory. 8 | 9 | But switching context will copy the stack the coroutine used. 10 | 11 | Read source for detail. 12 | 13 | Chinese blog : http://blog.codingnow.com/2012/07/c_coroutine.html 14 | 15 | ------------------------------------------------------------------------------------------ 16 | 17 | 云风实现的协程简单明了,非常适合入门协程. 18 | 19 | 笔者在这里fork了云风的coroutine库,根据自己的理解为代码添加了注释,方便自己理解协程的工作原理. 20 | -------------------------------------------------------------------------------- /coroutine.c: -------------------------------------------------------------------------------- 1 | #include "coroutine.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if __APPLE__ && __MACH__ 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #define STACK_SIZE (1024*1024) 16 | #define DEFAULT_COROUTINE 16 17 | 18 | struct coroutine; 19 | 20 | // 协程调度器 21 | struct schedule { 22 | char stack[STACK_SIZE]; 23 | ucontext_t main; // 正在running的协程在执行完后需切换到的上下文,由于是非对称协程,所以该上下文用来接管协程结束后的程序控制权 24 | int nco; // 调度器中已保存的协程数量 25 | int cap; // 调度器中协程的最大容量 26 | int running; // 调度器中正在running的协程id 27 | struct coroutine **co; // 连续内存空间,用于存储所有协程任务 28 | }; 29 | 30 | // 协程任务类型 31 | struct coroutine { 32 | coroutine_func func; // 协程函数 33 | void *ud; // 协程函数的参数(用户数据) 34 | ucontext_t ctx; // 协程上下文 35 | struct schedule * sch; // 协程所属的调度器 36 | // ptrdiff_t定义在stddef.h(cstddef)中,通常被定义为long int类型,通常用来保存两个指针减法操作的结果. 37 | ptrdiff_t cap; // 协程栈的最大容量 38 | ptrdiff_t size; // 协程栈的当前容量 39 | int status; // 协程状态(COROUTINE_DEAD/COROUTINE_READY/COROUTINE_RUNNING/COROUTINE_SUSPEND) 40 | char *stack; // 协程栈 41 | }; 42 | 43 | // 创建协程任务(分配内存空间)并初始化 44 | struct coroutine * 45 | _co_new(struct schedule *S , coroutine_func func, void *ud) { 46 | struct coroutine * co = malloc(sizeof(*co)); 47 | co->func = func; // 初始化协程函数 48 | co->ud = ud; // 初始化用户数据 49 | co->sch = S; // 初始化协程所属的调度器 50 | co->cap = 0; // 初始化协程栈的最大容量 51 | co->size = 0; // 初始化协程栈的当前容量 52 | co->status = COROUTINE_READY; // 初始化协程状态 53 | co->stack = NULL; // 初始化协程栈 54 | return co; 55 | } 56 | 57 | // 销毁协程任务(释放内存空间) 58 | void 59 | _co_delete(struct coroutine *co) { 60 | free(co->stack); 61 | free(co); 62 | } 63 | 64 | // 创建协程调度器schedule 65 | struct schedule * 66 | coroutine_open(void) { 67 | struct schedule *S = malloc(sizeof(*S)); // 从堆上为调度器分配内存空间 68 | S->nco = 0; // 初始化调度器的当前协程数量 69 | S->cap = DEFAULT_COROUTINE; // 初始化调度器的最大协程数量 70 | S->running = -1; 71 | S->co = malloc(sizeof(struct coroutine *) * S->cap); // 为调度器中的协程分配存储空间 72 | memset(S->co, 0, sizeof(struct coroutine *) * S->cap); 73 | return S; 74 | } 75 | 76 | // 销毁协程调度器schedule 77 | void 78 | coroutine_close(struct schedule *S) { 79 | int i; 80 | for (i=0;icap;i++) { 81 | struct coroutine * co = S->co[i]; 82 | if (co) { 83 | _co_delete(co); 84 | } 85 | } 86 | free(S->co); 87 | S->co = NULL; 88 | free(S); 89 | } 90 | 91 | // 创建协程任务、并将其加入调度器中 92 | int 93 | coroutine_new(struct schedule *S, coroutine_func func, void *ud) { 94 | // 创建协程任务(分配内存空间)并初始化 95 | struct coroutine *co = _co_new(S, func , ud); 96 | 97 | // 将协程任务co加入调度器S,并返回该协程任务的id 98 | if (S->nco >= S->cap) { 99 | // 调整调度器S中协程的最大容量,然后将协程任务co加入调度器S,并返回该协程任务的id 100 | int id = S->cap; 101 | S->co = realloc(S->co, S->cap * 2 * sizeof(struct coroutine *)); 102 | memset(S->co + S->cap , 0 , sizeof(struct coroutine *) * S->cap); 103 | S->co[S->cap] = co; 104 | S->cap *= 2; 105 | ++S->nco; 106 | return id; 107 | } else { 108 | // 将协程任务co加入调度器S,并返回该协程任务的id 109 | int i; 110 | for (i=0;icap;i++) { 111 | int id = (i+S->nco) % S->cap; 112 | if (S->co[id] == NULL) { 113 | S->co[id] = co; 114 | ++S->nco; 115 | return id; 116 | } 117 | } 118 | } 119 | assert(0); 120 | return -1; 121 | } 122 | 123 | // 所有新协程第一次执行时的入口函数(其中执行协程,并处理善后工作等) 124 | static void 125 | mainfunc(uint32_t low32, uint32_t hi32) { 126 | uintptr_t ptr = (uintptr_t)low32 | ((uintptr_t)hi32 << 32); 127 | struct schedule *S = (struct schedule *)ptr; 128 | int id = S->running; 129 | struct coroutine *C = S->co[id]; 130 | C->func(S,C->ud); 131 | _co_delete(C); 132 | S->co[id] = NULL; 133 | --S->nco; 134 | S->running = -1; 135 | } 136 | 137 | // 恢复协程号为id的协程任务 138 | void 139 | coroutine_resume(struct schedule * S, int id) { 140 | assert(S->running == -1); 141 | assert(id >=0 && id < S->cap); 142 | struct coroutine *C = S->co[id]; 143 | if (C == NULL) 144 | return; 145 | int status = C->status; 146 | switch(status) { 147 | case COROUTINE_READY: 148 | getcontext(&C->ctx); // 获取程序当前上下文 149 | C->ctx.uc_stack.ss_sp = S->stack; // 设置上下文C->ctx的栈 150 | C->ctx.uc_stack.ss_size = STACK_SIZE; // 设置上下文C->ctx的栈容量 151 | C->ctx.uc_link = &S->main; // 设置上下文C->ctx执行完后恢复到S->main上下文, 否则当前线程因没有上下文可执行而退出 152 | S->running = id; 153 | C->status = COROUTINE_RUNNING; 154 | uintptr_t ptr = (uintptr_t)S; 155 | makecontext(&C->ctx, (void (*)(void)) mainfunc, 2, (uint32_t)ptr, (uint32_t)(ptr>>32)); // 修改上下文C->ctx, 新的上下文中执行函数mainfunc 156 | swapcontext(&S->main, &C->ctx); // 保持当前上下文到S->main, 切换当前上下文为C->ctx 157 | break; 158 | case COROUTINE_SUSPEND: 159 | memcpy(S->stack + STACK_SIZE - C->size, C->stack, C->size); // 拷贝协程栈C->stack到S->stack 160 | S->running = id; // 设置当前运行的协程id 161 | C->status = COROUTINE_RUNNING; // 修改协程C的状态 162 | swapcontext(&S->main, &C->ctx); // 保存当前上下文到S->main, 切换当前上下文为C->ctx 163 | break; 164 | default: 165 | assert(0); 166 | } 167 | } 168 | 169 | // 保存协程栈 170 | static void 171 | _save_stack(struct coroutine *C, char *top) { 172 | char dummy = 0; 173 | assert(top - &dummy <= STACK_SIZE); 174 | if (C->cap < top - &dummy) { 175 | // 为协程栈分配内存空间 176 | free(C->stack); 177 | C->cap = top-&dummy; 178 | C->stack = malloc(C->cap); 179 | } 180 | 181 | C->size = top - &dummy; 182 | memcpy(C->stack, &dummy, C->size); // TODO - 不是很明白为什么这种方式可以保存协程栈 183 | } 184 | 185 | // 保存上下文后中断当前协程的执行,然后由调度器中的main上下文接管程序执行权 186 | void 187 | coroutine_yield(struct schedule * S) { 188 | int id = S->running; 189 | assert(id >= 0); 190 | struct coroutine * C = S->co[id]; 191 | assert((char *)&C > S->stack); 192 | _save_stack(C,S->stack + STACK_SIZE); // 保存协程栈 193 | C->status = COROUTINE_SUSPEND; // 修改协程状态 194 | S->running = -1; // 修改当前执行的协程id为-1 195 | swapcontext(&C->ctx , &S->main); // 保存当前协程的上下文到C->ctx, 切换当前上下文到S->main 196 | } 197 | 198 | // 根据协程任务id返回协程的当前状态 199 | int 200 | coroutine_status(struct schedule * S, int id) { 201 | assert(id>=0 && id < S->cap); 202 | if (S->co[id] == NULL) { 203 | return COROUTINE_DEAD; 204 | } 205 | return S->co[id]->status; 206 | } 207 | 208 | // 返回调度器S中正在running的协程任务id 209 | int 210 | coroutine_running(struct schedule * S) { 211 | return S->running; 212 | } 213 | -------------------------------------------------------------------------------- /coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef C_COROUTINE_H 2 | #define C_COROUTINE_H 3 | 4 | #define COROUTINE_DEAD 0 5 | #define COROUTINE_READY 1 6 | #define COROUTINE_RUNNING 2 7 | #define COROUTINE_SUSPEND 3 8 | 9 | struct schedule; // 前置声明协程调度器struct schedule类型 10 | 11 | typedef void (*coroutine_func)(struct schedule *, void *ud); // 声明一个函数指针类型 12 | 13 | struct schedule * coroutine_open(void); // 创建协程调度器 14 | void coroutine_close(struct schedule *); // 关闭协程调度器 15 | 16 | int coroutine_new(struct schedule *, coroutine_func, void *ud); // 创建协程任务,将其加入调度器中 17 | void coroutine_resume(struct schedule *, int id); // 恢复协程号为id的协程任务 18 | int coroutine_status(struct schedule *, int id); // 根据协程任务id返回协程的当前状态 19 | int coroutine_running(struct schedule *); // 返回调度器S中正在running的协程任务id 20 | void coroutine_yield(struct schedule *); // 保存当前上下文后中断当前协程的执行 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "coroutine.h" 2 | #include 3 | 4 | struct args { 5 | int n; 6 | }; 7 | 8 | static void 9 | foo(struct schedule * S, void *ud) { 10 | struct args * arg = ud; 11 | int start = arg->n; 12 | int i; 13 | for (i=0;i<5;i++) { 14 | printf("coroutine %d : %d\n",coroutine_running(S) , start + i); 15 | coroutine_yield(S); 16 | } 17 | } 18 | 19 | static void 20 | test(struct schedule *S) { 21 | struct args arg1 = { 0 }; 22 | struct args arg2 = { 100 }; 23 | 24 | int co1 = coroutine_new(S, foo, &arg1); 25 | int co2 = coroutine_new(S, foo, &arg2); 26 | printf("main start\n"); 27 | while (coroutine_status(S,co1) && coroutine_status(S,co2)) { 28 | coroutine_resume(S,co1); 29 | coroutine_resume(S,co2); 30 | } 31 | printf("main end\n"); 32 | } 33 | 34 | int 35 | main() { 36 | struct schedule * S = coroutine_open(); 37 | test(S); 38 | coroutine_close(S); 39 | 40 | return 0; 41 | } 42 | 43 | --------------------------------------------------------------------------------