├── Makefile ├── README ├── comain.cc ├── coroutine.cc └── coroutine.h /Makefile: -------------------------------------------------------------------------------- 1 | all : run 2 | 3 | run : comain.cc coroutine.cc 4 | g++ -g -Wall -o $@ $^ 5 | 6 | clean : 7 | rm run 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A simple coroutine library for c++, modeling coroutine from lua. 2 | 3 | usage: 4 | 1. create a schedluer: 5 | CoroutineScheduler* sched = new CoroutineScheduler(1024); 6 | 2. create coroutine: 7 | int id = sched->CreateCoroutine(func, arg); 8 | 3. run coroutine: 9 | uintptr_t ret = sched->ResumeCoroutine(id); 10 | 11 | 12 | -------------------------------------------------------------------------------- /comain.cc: -------------------------------------------------------------------------------- 1 | #include "coroutine.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | CoroutineScheduler* sched = NULL; 8 | 9 | uintptr_t func1(void* arg) 10 | { 11 | uintptr_t ret; 12 | cout << "function1 a now!,arg:" << arg << ", start to yield." << endl; 13 | ret = sched->Yield((uintptr_t)"func1 yield 1"); 14 | cout << "1.fun1 return from yield:" << (const char*)ret << endl; 15 | ret = sched->Yield((uintptr_t)"func1 yield 2"); 16 | cout << "2.fun1 return from yield:" << (const char*)ret << ", going to stop" << endl; 17 | 18 | return (uintptr_t)"func1 stop"; 19 | } 20 | 21 | uintptr_t func2(void* s) 22 | { 23 | cout << "function2 a now!, arg:" << s << ", start to yield." << endl; 24 | const char* y = (const char*)sched->Yield((uintptr_t)"func2 yield 1"); 25 | cout << "fun2 return from yield:" << y <<", going to stop" << endl; 26 | 27 | return (uintptr_t)"func2 stop"; 28 | } 29 | 30 | int main() 31 | { 32 | sched = new CoroutineScheduler(); 33 | 34 | bool stop = false; 35 | int f1 = sched->CreateCoroutine(func1, (void*)111); 36 | int f2 = sched->CreateCoroutine(func2, (void*)222); 37 | 38 | while (!stop) 39 | { 40 | stop = true; 41 | if (sched->IsCoroutineAlive(f1)) 42 | { 43 | stop = false; 44 | const char* y1 = (const char*)sched->ResumeCoroutine(f1, (uintptr_t)"resume func1"); 45 | cout << "func1 yield:" << y1 << endl; 46 | } 47 | 48 | if (sched->IsCoroutineAlive(f2)) 49 | { 50 | stop = false; 51 | const char* y2 = (const char*)sched->ResumeCoroutine(f2, (uintptr_t)"resume func2"); 52 | cout << "func2 yield:" << y2 << endl; 53 | } 54 | } 55 | 56 | delete sched; 57 | return 0; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /coroutine.cc: -------------------------------------------------------------------------------- 1 | #include "coroutine.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | struct coroutine 11 | { 12 | void* arg; 13 | 14 | int status; 15 | ucontext_t cxt; 16 | uintptr_t yield; 17 | CoroutineScheduler::CoFunc func; 18 | 19 | char stack[0]; 20 | }; 21 | 22 | class CoroutineScheduler::SchedulerImpl 23 | { 24 | public: 25 | 26 | explicit SchedulerImpl(int stacksize); 27 | ~SchedulerImpl(); 28 | 29 | int CreateCoroutine(CoroutineScheduler::CoFunc func, void* arg); 30 | int DestroyCoroutine(int id); 31 | 32 | uintptr_t Yield(uintptr_t); 33 | uintptr_t ResumeCoroutine(int id, uintptr_t); 34 | 35 | bool IsCoroutineAlive(int id) const; 36 | 37 | private: 38 | 39 | SchedulerImpl(const SchedulerImpl&); 40 | SchedulerImpl& operator=(const SchedulerImpl&); 41 | 42 | static void Schedule(void* arg); 43 | 44 | private: 45 | 46 | int index_; 47 | int running_; 48 | const int stacksize_; 49 | 50 | ucontext_t mainContext_; 51 | map id2routine_; 52 | coroutine** routine_; 53 | }; 54 | 55 | CoroutineScheduler::SchedulerImpl::SchedulerImpl(int stacksize) 56 | :index_(0), running_(-1), stacksize_(stacksize) 57 | { 58 | } 59 | 60 | CoroutineScheduler::SchedulerImpl::~SchedulerImpl() 61 | { 62 | map::iterator it = id2routine_.begin(); 63 | 64 | while (it != id2routine_.end()) 65 | { 66 | if (it->second) free (it->second); 67 | } 68 | } 69 | 70 | bool CoroutineScheduler::SchedulerImpl::IsCoroutineAlive(int id) const 71 | { 72 | map::const_iterator it = id2routine_.find(id); 73 | if (it == id2routine_.end()) return false; 74 | 75 | return it->second; 76 | } 77 | 78 | int CoroutineScheduler::SchedulerImpl::DestroyCoroutine(int id) 79 | { 80 | coroutine* cor = id2routine_[id]; 81 | if (!cor) return -1; 82 | 83 | free(cor); 84 | id2routine_.erase(id); 85 | return id; 86 | } 87 | 88 | // static function 89 | void CoroutineScheduler::SchedulerImpl::Schedule(void* arg) 90 | { 91 | assert(arg); 92 | SchedulerImpl* sched = (SchedulerImpl*) arg; 93 | 94 | int running = sched->running_; 95 | 96 | coroutine* cor = sched->id2routine_[running]; 97 | assert(cor); 98 | 99 | cor->yield = cor->func(cor->arg); 100 | 101 | sched->running_ = -1; 102 | cor->status = CO_FINISHED; 103 | } 104 | 105 | int CoroutineScheduler::SchedulerImpl::CreateCoroutine(CoroutineScheduler::CoFunc func, void* arg) 106 | { 107 | coroutine* cor = (coroutine*)malloc(sizeof(coroutine) + stacksize_); 108 | 109 | if (cor == NULL) return -1; 110 | 111 | cor->arg = arg; 112 | cor->func = func; 113 | cor->yield = 0; 114 | cor->status = CO_READY; 115 | 116 | int index = index_++; 117 | id2routine_[index] = cor; 118 | 119 | return index; 120 | } 121 | 122 | uintptr_t CoroutineScheduler::SchedulerImpl::ResumeCoroutine(int id, uintptr_t y) 123 | { 124 | coroutine* cor = id2routine_[id]; 125 | if (cor == NULL || cor->status == CO_RUNNING) return 0; 126 | 127 | cor->yield = y; 128 | switch (cor->status) 129 | { 130 | case CO_READY: 131 | { 132 | getcontext(&cor->cxt); 133 | 134 | cor->status = CO_RUNNING; 135 | cor->cxt.uc_stack.ss_sp = cor->stack; 136 | cor->cxt.uc_stack.ss_size = stacksize_; 137 | // sucessor context. 138 | cor->cxt.uc_link = &mainContext_; 139 | 140 | running_ = id; 141 | // setup coroutine context 142 | makecontext(&cor->cxt, (void (*)())Schedule, 1, this); 143 | swapcontext(&mainContext_, &cor->cxt); 144 | } 145 | break; 146 | case CO_SUSPENDED: 147 | { 148 | running_ = id; 149 | cor->status = CO_RUNNING; 150 | swapcontext(&mainContext_, &cor->cxt); 151 | } 152 | break; 153 | default: 154 | assert(0); 155 | } 156 | 157 | uintptr_t ret = cor->yield; 158 | cor->yield = 0; 159 | 160 | if (running_ == -1 && cor->status == CO_FINISHED) DestroyCoroutine(id); 161 | 162 | return ret; 163 | } 164 | 165 | uintptr_t CoroutineScheduler::SchedulerImpl::Yield(uintptr_t y) 166 | { 167 | if (running_ < 0) return 0; 168 | 169 | int cur = running_; 170 | running_ = -1; 171 | 172 | coroutine* cor = id2routine_[cur]; 173 | 174 | cor->yield = y; 175 | cor->status = CO_SUSPENDED; 176 | 177 | swapcontext(&cor->cxt, &mainContext_); 178 | 179 | uintptr_t ret = cor->yield; 180 | cor->yield = 0; 181 | return ret; 182 | } 183 | 184 | CoroutineScheduler::CoroutineScheduler(int stacksize) 185 | :impl_(new SchedulerImpl(stacksize)) 186 | { 187 | } 188 | 189 | CoroutineScheduler::~CoroutineScheduler() 190 | { 191 | delete impl_; 192 | } 193 | 194 | int CoroutineScheduler::CreateCoroutine(CoFunc func, void* arg) 195 | { 196 | return impl_->CreateCoroutine(func, arg); 197 | } 198 | 199 | int CoroutineScheduler::DestroyCoroutine(int id) 200 | { 201 | return impl_->DestroyCoroutine(id); 202 | } 203 | 204 | uintptr_t CoroutineScheduler::ResumeCoroutine(int id, uintptr_t y) 205 | { 206 | return impl_->ResumeCoroutine(id, y); 207 | } 208 | 209 | uintptr_t CoroutineScheduler::Yield(uintptr_t y) 210 | { 211 | return impl_->Yield(y); 212 | } 213 | 214 | bool CoroutineScheduler::IsCoroutineAlive(int id) const 215 | { 216 | return impl_->IsCoroutineAlive(id); 217 | } 218 | 219 | -------------------------------------------------------------------------------- /coroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef H_COROUTINE_H_ 2 | #define H_COROUTINE_H_ 3 | 4 | #include 5 | 6 | class CoroutineScheduler // non copyable 7 | { 8 | public: 9 | 10 | typedef uintptr_t (*CoFunc)(void* arg); 11 | 12 | enum Status 13 | { 14 | CO_READY, 15 | CO_SUSPENDED, 16 | CO_RUNNING, 17 | CO_FINISHED 18 | }; 19 | 20 | public: 21 | 22 | explicit CoroutineScheduler(int stacksize = 1024); 23 | ~CoroutineScheduler(); 24 | 25 | int DestroyCoroutine(int id); 26 | int CreateCoroutine(CoFunc func, void* arg); 27 | 28 | uintptr_t Yield(uintptr_t y = 0); 29 | uintptr_t ResumeCoroutine(int id, uintptr_t y = 0); 30 | 31 | bool IsCoroutineAlive(int id) const; 32 | 33 | private: 34 | 35 | CoroutineScheduler(const CoroutineScheduler&); 36 | CoroutineScheduler& operator=(const CoroutineScheduler&); 37 | 38 | private: 39 | 40 | class SchedulerImpl; 41 | SchedulerImpl* impl_; 42 | }; 43 | 44 | #endif 45 | 46 | --------------------------------------------------------------------------------