协程的学习二:C风格写的协程(以云凤的协程为例子讲解)

一、测试代码讲解

1)main函数主流程

1)创建调度器
struct schedule * S = coroutine_open();
2)测试test调度器
(1)创建协程并yield
int co1 = coroutine_new(S, foo, &arg1);
int co2 = coroutine_new(S, foo, &arg2);
(2)开启协程并按顺序resume恢复堆栈信息回来
	while (coroutine_status(S,co1) && coroutine_status(S,co2)) {
		coroutine_resume(S,co1);
		coroutine_resume(S,co2);
	} 
(3)关闭调度器
coroutine_close(S);

2)main测试代码示例

#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;
}

二、协程头文件coroutine.h

1)代码作用讲解

①协程执行函数

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);

⑥判断协程yield出去后是否准备resume回来

int coroutine_status(struct schedule *, int id);

⑦coroutine_running(struct schedule *)不知道什么作用

int coroutine_running(struct schedule *);

⑧协程让出CPU占用(yield)

void coroutine_yield(struct schedule *);

2)代码实体

#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

三、协程函数实现文件coroutine.c

0)备注背景知识

1)ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型。ptrdiff_t类型变量通常用来保存两个指针减法操作的结果

1)错误码及结构体

①struct args

struct args {
    
    
	int n;
};

②struct coroutine

struct coroutine {
    
    
	coroutine_func func;			//协程执行函数
	void *ud;
	ucontext_t ctx;					//协程上下文
	struct schedule * sch;			//协程调度器
	ptrdiff_t cap;					//ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型。ptrdiff_t类型变量通常用来保存两个指针减法操作的结果
	ptrdiff_t size;
	int status;
	char *stack;
};

③struct schedule

struct schedule {
    
    
	char stack[STACK_SIZE];		//占用的总栈大小
	ucontext_t main;			//上下文
	int nco;
	int cap;
	int running;
	struct coroutine **co;
};

2)代码

#include "coroutine.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdint.h>

#if __APPLE__ && __MACH__
	#include <sys/ucontext.h>
#else 
	#include <ucontext.h>
#endif 

#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->co = realloc(S->co, S->cap * 2 * sizeof(struct coroutine *));
		memset(S->co + S->cap , 0 , sizeof(struct coroutine *) * S->cap);
		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(uint32_t low32, uint32_t hi32) {
    
    
	uintptr_t ptr = (uintptr_t)low32 | ((uintptr_t)hi32 << 32);
	struct schedule *S = (struct schedule *)ptr;
	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;
		uintptr_t ptr = (uintptr_t)S;
		makecontext(&C->ctx, (void (*)(void)) mainfunc, 2, (uint32_t)ptr, (uint32_t)(ptr>>32));
		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;
}

四、云凤协程注意点

1)他实现的协程是无栈协程不用担心溢出的问题

原文:

Coroutines in the same schedule share the stack , so you can create many coroutines without worry about memory.

2)云凤的协程是用setcontext实现的,但是这只是在linux下,windows下他建议可以用windows的fiber去实现

猜你喜欢

转载自blog.csdn.net/weixin_43679037/article/details/121444824