Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Hello world

  • Loading branch information...
commit 871c6e37ba1e9071c3dc4ceb4066efe03a146481 0 parents
@cloudwu authored
Showing with 266 additions and 0 deletions.
  1. +7 −0 Makefile
  2. +11 −0 README.md
  3. +184 −0 coroutine.c
  4. +22 −0 coroutine.h
  5. +42 −0 main.c
7 Makefile
@@ -0,0 +1,7 @@
+all : main
+
+main : main.c coroutine.c
+ gcc -g -Wall -o $@ $^
+
+clean :
+ rm main
11 README.md
@@ -0,0 +1,11 @@
+It's an asymmetric coroutine library (like lua).
+
+You can use coroutine_open to open a schedule first, and then create coroutine in that schedule.
+
+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.
+
+Coroutines in the same schedule share the stack , so you can create many coroutines without worry about memory.
+
+But switching context will copy the stack the coroutine used.
+
+Read source for detail.
184 coroutine.c
@@ -0,0 +1,184 @@
+#include "coroutine.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ucontext.h>
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#define STACK_SIZE (1024*1024)
+#define DEFAULT_COROUTINE 16
+
+struct coroutine;
+
+struct schedule {
+ char stack[STACK_SIZE];
+ ucontext_t main;
+ int nco;
+ int cap;
+ int running;
+ struct coroutine **co;
+};
+
+struct coroutine {
+ coroutine_func func;
+ void *ud;
+ ucontext_t ctx;
+ struct schedule * sch;
+ ptrdiff_t cap;
+ ptrdiff_t size;
+ int status;
+ char *stack;
+};
+
+struct coroutine *
+_co_new(struct schedule *S , coroutine_func func, void *ud) {
+ struct coroutine * co = malloc(sizeof(*co));
+ co->func = func;
+ co->ud = ud;
+ co->sch = S;
+ co->cap = 0;
+ co->size = 0;
+ co->status = COROUTINE_READY;
+ co->stack = NULL;
+ return co;
+}
+
+void
+_co_delete(struct coroutine *co) {
+ free(co->stack);
+ free(co);
+}
+
+struct schedule *
+coroutine_open(void) {
+ struct schedule *S = malloc(sizeof(*S));
+ S->nco = 0;
+ S->cap = DEFAULT_COROUTINE;
+ S->running = -1;
+ S->co = malloc(sizeof(struct coroutine *) * S->cap);
+ memset(S->co, 0, sizeof(struct coroutine *) * S->cap);
+ return S;
+}
+
+void
+coroutine_close(struct schedule *S) {
+ int i;
+ for (i=0;i<S->cap;i++) {
+ struct coroutine * co = S->co[i];
+ if (co) {
+ _co_delete(co);
+ }
+ }
+ free(S->co);
+ S->co = NULL;
+ free(S);
+}
+
+int
+coroutine_new(struct schedule *S, coroutine_func func, void *ud) {
+ struct coroutine *co = _co_new(S, func , ud);
+ if (S->nco >= S->cap) {
+ int id = S->cap;
+ S->cap *= 2;
+ S->co = realloc(S->co, S->cap * 2 * sizeof(struct coroutine *));
+ memset(S->co + S->cap , 0 , sizeof(struct coroutine *) * 2);
+ S->co[S->cap] = co;
+ S->cap *= 2;
+ ++S->nco;
+ return id;
+ } else {
+ int i;
+ for (i=0;i<S->cap;i++) {
+ int id = (i+S->nco) % S->cap;
+ if (S->co[id] == NULL) {
+ S->co[id] = co;
+ ++S->nco;
+ return id;
+ }
+ }
+ }
+ assert(0);
+ return -1;
+}
+
+static void
+mainfunc(struct schedule *S) {
+ int id = S->running;
+ struct coroutine *C = S->co[id];
+ C->func(S,C->ud);
+ _co_delete(C);
+ S->co[id] = NULL;
+ --S->nco;
+ S->running = -1;
+}
+
+void
+coroutine_resume(struct schedule * S, int id) {
+ assert(S->running == -1);
+ assert(id >=0 && id < S->cap);
+ struct coroutine *C = S->co[id];
+ if (C == NULL)
+ return;
+ int status = C->status;
+ switch(status) {
+ case COROUTINE_READY:
+ getcontext(&C->ctx);
+ C->ctx.uc_stack.ss_sp = S->stack;
+ C->ctx.uc_stack.ss_size = STACK_SIZE;
+ C->ctx.uc_link = &S->main;
+ S->running = id;
+ C->status = COROUTINE_RUNNING;
+ makecontext(&C->ctx, (void (*)(void)) mainfunc, 1, S);
+ swapcontext(&S->main, &C->ctx);
+ break;
+ case COROUTINE_SUSPEND:
+ memcpy(S->stack + STACK_SIZE - C->size, C->stack, C->size);
+ S->running = id;
+ C->status = COROUTINE_RUNNING;
+ swapcontext(&S->main, &C->ctx);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void
+_save_stack(struct coroutine *C, char *top) {
+ char dummy = 0;
+ assert(top - &dummy <= STACK_SIZE);
+ if (C->cap < top - &dummy) {
+ free(C->stack);
+ C->cap = top-&dummy;
+ C->stack = malloc(C->cap);
+ }
+ C->size = top - &dummy;
+ memcpy(C->stack, &dummy, C->size);
+}
+
+void
+coroutine_yield(struct schedule * S) {
+ int id = S->running;
+ assert(id >= 0);
+ struct coroutine * C = S->co[id];
+ assert((char *)&C > S->stack);
+ _save_stack(C,S->stack + STACK_SIZE);
+ C->status = COROUTINE_SUSPEND;
+ S->running = -1;
+ swapcontext(&C->ctx , &S->main);
+}
+
+int
+coroutine_status(struct schedule * S, int id) {
+ assert(id>=0 && id < S->cap);
+ if (S->co[id] == NULL) {
+ return COROUTINE_DEAD;
+ }
+ return S->co[id]->status;
+}
+
+int
+coroutine_running(struct schedule * S) {
+ return S->running;
+}
+
22 coroutine.h
@@ -0,0 +1,22 @@
+#ifndef C_COROUTINE_H
+#define C_COROUTINE_H
+
+#define COROUTINE_DEAD 0
+#define COROUTINE_READY 1
+#define COROUTINE_RUNNING 2
+#define COROUTINE_SUSPEND 3
+
+struct schedule;
+
+typedef void (*coroutine_func)(struct schedule *, void *ud);
+
+struct schedule * coroutine_open(void);
+void coroutine_close(struct schedule *);
+
+int coroutine_new(struct schedule *, coroutine_func, void *ud);
+void coroutine_resume(struct schedule *, int id);
+int coroutine_status(struct schedule *, int id);
+int coroutine_running(struct schedule *);
+void coroutine_yield(struct schedule *);
+
+#endif
42 main.c
@@ -0,0 +1,42 @@
+#include "coroutine.h"
+#include <stdio.h>
+
+struct args {
+ int n;
+};
+
+static void
+foo(struct schedule * S, void *ud) {
+ struct args * arg = ud;
+ int start = arg->n;
+ int i;
+ for (i=0;i<5;i++) {
+ printf("coroutine %d : %d\n",coroutine_running(S) , start + i);
+ coroutine_yield(S);
+ }
+}
+
+static void
+test(struct schedule *S) {
+ struct args arg1 = { 0 };
+ struct args arg2 = { 100 };
+
+ int co1 = coroutine_new(S, foo, &arg1);
+ int co2 = coroutine_new(S, foo, &arg2);
+ printf("main start\n");
+ while (coroutine_status(S,co1) && coroutine_status(S,co2)) {
+ coroutine_resume(S,co1);
+ coroutine_resume(S,co2);
+ }
+ printf("main end\n");
+}
+
+int
+main() {
+ struct schedule * S = coroutine_open();
+ test(S);
+ coroutine_close(S);
+
+ return 0;
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.