co_routine(第三部分:协程各种操作)—— libco源码分析、学习笔记

博客一级目录

二级目录——libco源码分析/学习笔记

由于本源代码蛮长的,所以按照功能划分模块来分析,分为若干部分,详见二级目录↑

代码文件:co_routine.hco_routine.cppco_routine_inner.h

三、协程各种操作

void co_yield_env( stCoRoutineEnv_t *env );//将当前执行协程从协程栈中出栈并将执行权交给栈内上一个协程。

void co_yield_env( stCoRoutineEnv_t *env )
{
	
	stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ];
	stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ];

	env->iCallStackSize--;

	co_swap( curr, last);
}
//切出到主协程
void co_yield_ct()
{
	co_yield_env( co_get_curr_thread_env() );
}
//跟co_yield_env功能一样只是参数不同,编程时常用。
void co_yield( stCoRoutine_t *co )
{
	co_yield_env( co->env );
}

static int CoRoutineFunc( stCoRoutine_t *co,void * );//被co_resume作为协程入口函数,可以做一些协程流程基本信息的填写,比如是否是第一次执行、协程退出后执行权的交接。

函数内调用了pfn函数(pfn函数就是协程的主函数)
执行完毕后cEnd标记为1

这里修改了子协程的env就完成了出栈工作,env是全局共享的。

static int CoRoutineFunc( stCoRoutine_t *co,void * )
{
	if( co->pfn )
	{
		co->pfn( co->arg );//执行主函数,内部允许嵌套有限数量的协程调用
	}
	co->cEnd = 1;//执行完毕后标记为1

	stCoRoutineEnv_t *env = co->env;//env是指针,修改后co的原内容也同步改变。其实下面可以直接写co->env,因为下面的函数没有改变env指针值。

	co_yield_env( env );//将当前执行的co从协程栈中出栈并将执行权交给父协程。

	return 0;
}

void co_free( stCoRoutine_t *co );//负责协程销毁工作。

void co_free( stCoRoutine_t *co )
{
    if (!co->cIsShareStack) 
    {    
        free(co->stack_mem->stack_buffer);
        free(co->stack_mem);
    }   
    free( co );
}

void co_release( stCoRoutine_t *co );//销毁co

void co_release( stCoRoutine_t *co )//销毁co但是不销毁用户栈
{
    co_free( co );
}

void save_stack_buffer(stCoRoutine_t* occupy_co);//用于备份用户栈有效数据。

void save_stack_buffer(stCoRoutine_t* occupy_co)
{
	///copy out
	stStackMem_t* stack_mem = occupy_co->stack_mem;//取用户栈
	int len = stack_mem->stack_bp - occupy_co->stack_sp;//计算有效数据长度

	if (occupy_co->save_buffer)
	{
		free(occupy_co->save_buffer), occupy_co->save_buffer = NULL;
	}

	occupy_co->save_buffer = (char*)malloc(len); //malloc buf;
	occupy_co->save_size = len;

	memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len);
}

交换cpu上执行的协程。

如果协程使用了共享栈,则在必要的时候保存和还原栈内数据。

void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co)
{
 	stCoRoutineEnv_t* env = co_get_curr_thread_env();

	//get curr stack sp
	char c;
	curr->stack_sp= &c;	//取当前栈顶,局部变量的地址就是栈顶喽,然后本函数后面也用了栈,不过函数过后丢掉就好了,不用保存。

	if (!pending_co->cIsShareStack) //不使用share stack自然就不存在当前占用共享栈的协程和即将占用共享栈的协程。
	{	//见官方注释:for copy stack log lastco and nextco
		env->pending_co = NULL;	 
		env->occupy_co = NULL;  
	}
	else //occupy_co是原来share stack的所有者,pending要抢占share stack  (多个协程争抢同一块share stack)
	{
		env->pending_co = pending_co;//设置好pending_co,下文中会将env->pending_co的栈还原
		//get last occupy co on the same stack mem
		stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co;//公共栈空间原来的所有者
		//(occupy_co初始化是NULL),第一次执行协程后这里就会把它变为自己。初始化后第一次拿到执行权栈不为空之后,它不再会是NULL。
		//set pending co to occupy thest stack mem;
		pending_co->stack_mem->occupy_co = pending_co; //不管之前是谁占用了这个地盘,pending都会抢占share stack

		env->occupy_co = occupy_co;	//记录下之前是谁在使用share stack
		
		//如果pending要抢占share stack 那原来的所有者
		if (occupy_co && occupy_co != pending_co)//有occupy_co并且不是自己
		{
			save_stack_buffer(occupy_co);//将occupy_co的栈中有效数据保存到occupy->save_buffer中
			//pending_co把occupy_co撵走之后就可以还原自己的栈空间了。
		}
	}
	
	//swap context
	coctx_swap(&(curr->ctx),&(pending_co->ctx));
	
	//stack buffer may be overwrite, so get again;		//why?
	stCoRoutineEnv_t* curr_env = co_get_curr_thread_env();
	stCoRoutine_t* update_occupy_co =  curr_env->occupy_co;
	stCoRoutine_t* update_pending_co = curr_env->pending_co;
	
	if (update_occupy_co && update_pending_co && update_occupy_co != update_pending_co)
	{
		//resume stack buffer
		if (update_pending_co->save_buffer && update_pending_co->save_size > 0)
		{
			memcpy(update_pending_co->stack_sp, update_pending_co->save_buffer, update_pending_co->save_size);
		}//pending抢占share stack
	}
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值