co_routine.cpp分析

co_routine.cpp


#include "co_routine.h"
#include "co_routine_inner.h"
#include "co_epoll.h"

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

extern "C"
{
	extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap");
};//声明coctx_swap函数,调用coctx_swap汇编代码。
using namespace std;
stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env );//获取当前协程
struct stCoEpoll_t;//管理定时事件的结构体

struct stCoRoutineEnv_t //管理协程环境的结构体
{
	stCoRoutine_t *pCallStack[ 128 ];//调用栈,添加协程则入栈,销毁协成则出栈。
	int iCallStackSize;//栈的大小
	stCoEpoll_t *pEpoll;//指向程序中负责管理定时事件的变量

	//for copy stack log lastco and nextco
	stCoRoutine_t* pending_co;//等待的协程
	stCoRoutine_t* occupy_co;//正在运行的协程
};
//int socket(int domain, int type, int protocol);
void co_log_err( const char *fmt,... )//错误日志
{
}


#if defined( __LIBCO_RDTSCP__)
static unsigned long long counter(void) 
{
	register uint32_t lo, hi;
	register unsigned long long o;
	__asm__ __volatile__ (
			"rdtscp" : "=a"(lo), "=d"(hi)::"%rcx"
			);
	o = hi;
	o <<= 32;
	return (o | lo);

}
static unsigned long long getCpuKhz()
{
	FILE *fp = fopen("/proc/cpuinfo","r");
	if(!fp) return 1;
	char buf[4096] = {0};
	fread(buf,1,sizeof(buf),fp);
	fclose(fp);

	char *lp = strstr(buf,"cpu MHz");
	if(!lp) return 1;
	lp += strlen("cpu MHz");
	while(*lp == ' ' || *lp == '\t' || *lp == ':')
	{
		++lp;
	}

	double mhz = atof(lp);
	unsigned long long u = (unsigned long long)(mhz * 1000);
	return u;
}
#endif

static unsigned long long GetTickMS()//获取时间
{
#if defined( __LIBCO_RDTSCP__)
	static uint32_t khz = getCpuKhz();
	return counter() / khz;
#else
	struct timeval now = { 0 };
	gettimeofday( &now,NULL );
	unsigned long long u = now.tv_sec;
	u *= 1000;
	u += now.tv_usec / 1000;
	return u;
#endif
}

/* no longer use
static pid_t GetPid()
{
    static __thread pid_t pid = 0;
    static __thread pid_t tid = 0;
    if( !pid || !tid || pid != getpid() )
    {
        pid = getpid();
#if defined( __APPLE__ )
		tid = syscall( SYS_gettid );
		if( -1 == (long)tid )
		{
			tid = pid;
		}
#elif defined( __FreeBSD__ )
		syscall(SYS_thr_self, &tid);
		if( tid < 0 )
		{
			tid = pid;
		}
#else
        tid = syscall( __NR_gettid );
#endif

    }
    return tid;

}
static pid_t GetPid()
{
	char **p = (char**)pthread_self();
	return p ? *(pid_t*)(p + 18) : getpid();
}
*/
template <class T,class TLink>
void RemoveFromLink(T *ap) //把这个元素从链表中删除,这里使用了模板类,epoll cond timeout均可以使用。
{
	TLink *lst = ap->pLink;
	if(!lst) return ;
	assert( lst->head && lst->tail );

	if( ap == lst->head )
	{
		lst->head = ap->pNext;
		if(lst->head)
		{
			lst->head->pPrev = NULL;
		}
	}
	else
	{
		if(ap->pPrev)
		{
			ap->pPrev->pNext = ap->pNext;
		}
	}

	if( ap == lst->tail )
	{
		lst->tail = ap->pPrev;
		if(lst->tail)
		{
			lst->tail->pNext = NULL;
		}
	}
	else
	{
		ap->pNext->pPrev = ap->pPrev;
	}

	ap->pPrev = ap->pNext = NULL;
	ap->pLink = NULL;
}

template <class TNode,class TLink>
void inline AddTail(TLink*apLink,TNode *ap) //添加元素到链表中
{
	if( ap->pLink )
	{
		return ;
	}
	if(apLink->tail)
	{
		apLink->tail->pNext = (TNode*)ap;
		ap->pNext = NULL;
		ap->pPrev = apLink->tail;
		apLink->tail = ap;
	}
	else
	{
		apLink->head = apLink->tail = ap;
		ap->pNext = ap->pPrev = NULL;
	}
	ap->pLink = apLink;
}
template <class TNode,class TLink>
void inline PopHead( TLink*apLink )//删除链表头
{
	if( !apLink->head )
	{
		return ;
	}
	TNode *lp = apLink->head;
	if( apLink->head == apLink->tail )
	{
		apLink->head = apLink->tail = NULL;
	}
	else
	{
		apLink->head = apLink->head->pNext;
	}

	lp->pPrev = lp->pNext = NULL;
	lp->pLink = NULL;

	if( apLink->head )
	{
		apLink->head->pPrev = NULL;
	}
}

template <class TNode,class TLink>
void inline Join( TLink*apLink,TLink *apOther )//添加ap0ther到aplink中
{
	//printf("apOther %p\n",apOther);
	if( !apOther->head )
	{
		return ;
	}
	TNode *lp = apOther->head;
	while( lp )
	{
		lp->pLink = apLink;
		lp = lp->pNext;
	}
	lp = apOther->head;
	if(apLink->tail)
	{
		apLink->tail->pNext = (TNode*)lp;
		lp->pPrev = apLink->tail;
		apLink->tail = apOther->tail;
	}
	else
	{
		apLink->head = apOther->head;
		apLink->tail = apOther->tail;
	}

	apOther->head = apOther->tail = NULL;
}

/for copy stack //
stStackMem_t* co_alloc_stackmem(unsigned int stack_size) //为每个共享栈分配内存
{
	stStackMem_t* stack_mem = (stStackMem_t*)malloc(sizeof(stStackMem_t));
	stack_mem->occupy_co= NULL;
	stack_mem->stack_size = stack_size;
	stack_mem->stack_buffer = (char*)malloc(stack_size);
	stack_mem->stack_bp = stack_mem->stack_buffer + stack_size;
	return stack_mem;
}

stShareStack_t* co_alloc_sharestack(int count, int stack_size) //分配count个共享栈 大小为stack_size。
{
	stShareStack_t* share_stack = (stShareStack_t*)malloc(sizeof(stShareStack_t)); //申请一个stShareStack_t管理共享栈
	share_stack->alloc_idx = 0;//初始idx为0,分配方式为循环遍历,根据idx的值。
	share_stack->stack_size = stack_size;//设置栈大小

	//alloc stack array
	share_stack->count = count;//设置栈数量
	stStackMem_t** stack_array = (stStackMem_t**)calloc(count, sizeof(stStackMem_t*));//申请count个结构体
	for (int i = 0; i < count; i++)
	{
		stack_array[i] = co_alloc_stackmem(stack_size);//为每个结构体初始化和分配内存
	}
	share_stack->stack_array = stack_array;//设置栈的地址
	return share_stack;
}

static stStackMem_t* co_get_stackmem(stShareStack_t* share_stack) //获取一个共享栈
{
	if (!share_stack)
	{
		return NULL;
	}
	int idx = share_stack->alloc_idx % share_stack->count; //一共count个栈,所以对count取摸,栈重复使用。
	share_stack->alloc_idx++;//idx++下一次申请使用下一个共享栈

	return share_stack->stack_array[idx];//返回申请的共享栈的地址
}


// ----------------------------------------------------------------------------
struct stTimeoutItemLink_t;
struct stTimeoutItem_t;
struct stCoEpoll_t //管理定时时间的结构体
{
	int iEpollFd;//epoll的描述符fd
	static const int _EPOLL_SIZE = 1024 * 10;//epoll可添加的时间数量

	struct stTimeout_t *pTimeout;//超时事件

	struct stTimeoutItemLink_t *pstTimeoutList;//从命名来看是超时时间的链表,可是并没有发现有实际使用。

	struct stTimeoutItemLink_t *pstActiveList;//已触发时间列表 

	co_epoll_res *result;//使用时epoll返回的结果存储的变量

};
typedef void (*OnPreparePfn_t)( stTimeoutItem_t *,struct epoll_event &ev, stTimeoutItemLink_t *active );//函数指针,源码中指向OnPollPreparePfn函数
typedef void (*OnProcessPfn_t)( stTimeoutItem_t *);//函数指针,源码中指向OnSignalProcessEvent函数
struct stTimeoutItem_t//stTimeoutItem_t结构体存储回调函数以及其他属性。
{

	enum
	{
		eMaxTimeout = 40 * 1000 //40s 命名像最大定时时间,实际为使用
	};
	stTimeoutItem_t *pPrev;//上一个stTimeoutItem_t
	stTimeoutItem_t *pNext;//下一个stTimeoutItem_t
	stTimeoutItemLink_t *pLink;//控制这个链表的结构体

	unsigned long long ullExpireTime;//时间超时时间=now+timeout

	OnPreparePfn_t pfnPrepare;//函数指针,源码中指向OnPollPreparePfn函数
	OnProcessPfn_t pfnProcess;//函数指针,源码中指向OnSignalProcessEvent函数

	void *pArg; // routine 指向当前协程
	bool bTimeout;//是否超时
};
struct stTimeoutItemLink_t //管理stTimeoutItem_t的链表
{
	stTimeoutItem_t *head;//表头
	stTimeoutItem_t *tail;//表尾

};
struct stTimeout_t//管理 stTimeoutItemLink_t的结构体
{
	stTimeoutItemLink_t *pItems;//指向一个 stTimeoutItemLink_t
	int iItemSize;//链表大小,限制莲

	unsigned long long ullStart;//开始时间
	long long llStartIdx;//当前的idx idx和ullstart会随着运行进行变化。
						//时间轮概念客参考该博客(https://blog.csdn.net/GreyBtfly/article/details/83688996)
};
stTimeout_t *AllocTimeout( int iSize )//初始化一个stTimeout_t
{
	stTimeout_t *lp = (stTimeout_t*)calloc( 1,sizeof(stTimeout_t) );//分配内存

	lp->iItemSize = iSize;//设置最大加入的大小。
	lp->pItems = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) * lp->iItemSize );//分配内存

	lp->ullStart = GetTickMS();//设置时间
	lp->llStartIdx = 0;

	return lp;
}
void FreeTimeout( stTimeout_t *apTimeout )//释放一个stTimeout_t
{
	free( apTimeout->pItems );
	free ( apTimeout );
}
int AddTimeout( stTimeout_t *apTimeout,stTimeoutItem_t *apItem ,unsigned long long allNow )//添加超时时间
{
	if( apTimeout->ullStart == 0 )//如果ullStart为0,则设置其为allNow
	{
		apTimeout->ullStart = allNow;
		apTimeout->llStartIdx = 0;
	}
	if( allNow < apTimeout->ullStart )//如果allNow小于stTimeout_t的ullStart时间,则产生错误日志。
	{
		co_log_err("CO_ERR: AddTimeout line %d allNow %llu apTimeout->ullStart %llu",
					__LINE__,allNow,apTimeout->ullStart);

		return __LINE__;
	}
	if( apItem->ullExpireTime < allNow )//如果添加的事件的超时时间小于allNow时间,则产生错误日志。
	{
		co_log_err("CO_ERR: AddTimeout line %d apItem->ullExpireTime %llu allNow %llu apTimeout->ullStart %llu",
					__LINE__,apItem->ullExpireTime,allNow,apTimeout->ullStart);

		return __LINE__;
	}
	unsigned long long diff = apItem->ullExpireTime - apTimeout->ullStart;//diff为应该在时间轮中加入的位置

	if( diff >= (unsigned long long)apTimeout->iItemSize )//如果diff大于最大的时间,重置并产生错误日志
	{
		diff = apTimeout->iItemSize - 1;
		co_log_err("CO_ERR: AddTimeout line %d diff %d",
					__LINE__,diff);

		//return __LINE__;
	}
	AddTail( apTimeout->pItems + ( apTimeout->llStartIdx + diff ) % apTimeout->iItemSize , apItem );//添加到stTimeout_t中。

	return 0;
}
inline void TakeAllTimeout( stTimeout_t *apTimeout,unsigned long long allNow,stTimeoutItemLink_t *apResult )
{
	if( apTimeout->ullStart == 0 )
	{
		apTimeout->ullStart = allNow;
		apTimeout->llStartIdx = 0;
	}

	if( allNow < apTimeout->ullStart )
	{
		return ;
	}
	int cnt = allNow - apTimeout->ullStart + 1;
	if( cnt > apTimeout->iItemSize )
	{
		cnt = apTimeout->iItemSize;
	}
	if( cnt < 0 )
	{
		return;
	}
	for( int i = 0;i<cnt;i++)
	{
		int idx = ( apTimeout->llStartIdx + i) % apTimeout->iItemSize;
		Join<stTimeoutItem_t,stTimeoutItemLink_t>( apResult,apTimeout->pItems + idx  );//join之后会置NULL,所以不会重复添加上一轮的事件。
	}
	apTimeout->ullStart = allNow;
	apTimeout->llStartIdx += cnt - 1;


}//取出时间片中的所有超时事件。简述一下就是添加从ullstart到allnow的事件,并更新ullStart和idx的值
static int CoRoutineFunc( stCoRoutine_t *co,void * )
{
	if( co->pfn )
	{
		co->pfn( co->arg );
	}
	co->cEnd = 1;

	stCoRoutineEnv_t *env = co->env;

	co_yield_env( env );

	return 0;
}//协程调用的函数,运行之后让出协程。用于协程恢复。



struct stCoRoutine_t *co_create_env( stCoRoutineEnv_t * env, const stCoRoutineAttr_t* attr,
		pfn_co_routine_t pfn,void *arg )//初始化协程
{

	stCoRoutineAttr_t at;
	if( attr )//如果attr参数不为NULL,即有共享栈,把共享栈信息复制到at中,
	{
		memcpy( &at,attr,sizeof(at) );
	}
	if( at.stack_size <= 0 )//如果小于等于0,复制128×1024即128k
	{
		at.stack_size = 128 * 1024;
	}
	else if( at.stack_size > 1024 * 1024 * 8 ) //如果大于8M,重置为8M
	{
		at.stack_size = 1024 * 1024 * 8;
	}

	if( at.stack_size & 0xFFF )//如果低位的值,则进位。即0x1001会编程0x2000 4k对齐 最小单位是扇区4k
	{
		at.stack_size &= ~0xFFF;//低12位清零
		at.stack_size += 0x1000;//加0x1000
	}

	stCoRoutine_t *lp = (stCoRoutine_t*)malloc( sizeof(stCoRoutine_t) );//分配内存

	memset( lp,0,(long)(sizeof(stCoRoutine_t)));//清空置0


	lp->env = env;
	lp->pfn = pfn;
	lp->arg = arg;//初始化env,pfn,arg属性

	stStackMem_t* stack_mem = NULL;//初始化
	if( at.share_stack )
	{
		stack_mem = co_get_stackmem( at.share_stack);
		at.stack_size = at.share_stack->stack_size;//如果有共享栈。分配共享栈,并设置大小。
	}
	else
	{
		stack_mem = co_alloc_stackmem(at.stack_size);//没有共享栈,分配内存
	}
	lp->stack_mem = stack_mem;//设置指向的栈

	lp->ctx.ss_sp = stack_mem->stack_buffer;//初始地址
	lp->ctx.ss_size = at.stack_size;//大小

	lp->cStart = 0;//是否开始,用来判断一个协成是否初始化过ctx
	lp->cEnd = 0;//是否结束
	lp->cIsMain = 0;//是不是主协程
	lp->cEnableSysHook = 0;//是否hook过
	lp->cIsShareStack = at.share_stack != NULL;//是不是共享栈

	lp->save_size = 0;//使用共享栈时会使用的属性,如何使用后面会介绍,协程如何使用共享栈的内存。
	lp->save_buffer = NULL;

	return lp;
}

int co_create( stCoRoutine_t **ppco,const stCoRoutineAttr_t *attr,pfn_co_routine_t pfn,void *arg )//创建协程
{
	if( !co_get_curr_thread_env() )//如果env没有初始化过,则初始化env,会创建主协程。
	{
		co_init_curr_thread_env();
	}
	stCoRoutine_t *co = co_create_env( co_get_curr_thread_env(), attr, pfn,arg );//初始化协程
	*ppco = co;
	return 0;
}
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_free( co );
}

void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co);//交换协程,pending_co协程取代curr协程

void co_resume( stCoRoutine_t *co )//启动协程
{
	stCoRoutineEnv_t *env = co->env;//设置env
	stCoRoutine_t *lpCurrRoutine = env->pCallStack[ env->iCallStackSize - 1 ];//lp为当前运行的协程
	if( !co->cStart )//如果没有初始化过,初始化ctx
	{
		coctx_make( &co->ctx,(coctx_pfn_t)CoRoutineFunc,co,0 );
		co->cStart = 1;
	}
	env->pCallStack[ env->iCallStackSize++ ] = co;//协程入栈
	co_swap( lpCurrRoutine, co );//交换协程


}
void co_yield_env( stCoRoutineEnv_t *env )//yield协程,协程会出调用栈,因此如果没有使用poll或者epoll,当前协程将不会使用
{

	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() );
}
void co_yield( stCoRoutine_t *co )//让出协程 libco的协程是按照调用栈的顺序运行的,所以co其实没有意义,
								//当然也有没有使用调用栈的,不过我没学习过x
{
	co_yield_env( co->env );
}

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);//保存数据
}

void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co)//交换协程
{
 	stCoRoutineEnv_t* env = co_get_curr_thread_env();//获取env

	//get curr stack sp
	char c;
	curr->stack_sp= &c;//获取栈地址 第一个定义的变量的地址就是栈地址。

	if (!pending_co->cIsShareStack)//如果不是共享栈
	{
		env->pending_co = NULL;
		env->occupy_co = NULL;
	}
	else
	{
		env->pending_co = pending_co;
		//get last occupy co on the same stack mem
		stCoRoutine_t* occupy_co = pending_co->stack_mem->occupy_co;
		//set pending co to occupy thest stack mem;
		pending_co->stack_mem->occupy_co = pending_co;//从定义来看是获取上一个occupy协程,并设置pending协成为occupy协程,但是我翻了一下源码,只发现了这个stack_mem->occupy_co= NULL;0-0

		env->occupy_co = occupy_co;
		if (occupy_co && occupy_co != pending_co)//如果两个协程不是同一个,则保存数据。
		{
			save_stack_buffer(occupy_co);
		}
	}

	//swap context
	coctx_swap(&(curr->ctx),&(pending_co->ctx) );//交换协程

	//stack buffer may be overwrite, so get again;
	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);//恢复数据
		}
	}
}



//int poll(struct pollfd fds[], nfds_t nfds, int timeout);
// { fd,events,revents }
struct stPollItem_t ;
struct stPoll_t : public stTimeoutItem_t
{
	struct pollfd *fds;//指向管理的fd
	nfds_t nfds; // typedef unsigned long int nfds_t; fd数量

	stPollItem_t *pPollItems;//指向管理他的item

	int iAllEventDetach;//是否分离的属性

	int iEpollFd;//指向管理事件的iepollfd

	int iRaiseCnt;//计数功能,在OnPollPreparePfn中自增,具体作用不清楚


};
struct stPollItem_t : public stTimeoutItem_t
{
	struct pollfd *pSelf;//自己的fd
	stPoll_t *pPoll;指向管理的stpoll_t

	struct epoll_event stEvent;//需要监听的事件
};
/*
 *   EPOLLPRI 		POLLPRI    // There is urgent data to read.
 *   EPOLLMSG 		POLLMSG
 *
 *   				POLLREMOVE
 *   				POLLRDHUP
 *   				POLLNVAL
 *
 * */
static uint32_t PollEvent2Epoll( short events )//poll监听事件改为epoll格式
{
	uint32_t e = 0;
	if( events & POLLIN ) 	e |= EPOLLIN;
	if( events & POLLOUT )  e |= EPOLLOUT;
	if( events & POLLHUP ) 	e |= EPOLLHUP;
	if( events & POLLERR )	e |= EPOLLERR;
	if( events & POLLRDNORM ) e |= EPOLLRDNORM;
	if( events & POLLWRNORM ) e |= EPOLLWRNORM;
	return e;
}
static short EpollEvent2Poll( uint32_t events )//epoll监听事件改为poll格式
{
	short e = 0;
	if( events & EPOLLIN ) 	e |= POLLIN;
	if( events & EPOLLOUT ) e |= POLLOUT;
	if( events & EPOLLHUP ) e |= POLLHUP;
	if( events & EPOLLERR ) e |= POLLERR;
	if( events & EPOLLRDNORM ) e |= POLLRDNORM;
	if( events & EPOLLWRNORM ) e |= POLLWRNORM;
	return e;
}

static __thread stCoRoutineEnv_t* gCoEnvPerThread = NULL;	
					/*
						在GUN标准中,提供了__thread关键字,配合static后,可以实现让一个线程拥有自己的全局变量
						所以无论什么时候,协程都是作用于线程上的。线程之间会不同。
						https://blog.csdn.net/wsj18808050/article/details/51603006
					*/

void co_init_curr_thread_env()//初始化线程中的env
{
	gCoEnvPerThread = (stCoRoutineEnv_t*)calloc( 1, sizeof(stCoRoutineEnv_t) );//分配内存
	stCoRoutineEnv_t *env = gCoEnvPerThread;//设置env

	env->iCallStackSize = 0;//调用栈大小
	struct stCoRoutine_t *self = co_create_env( env, NULL, NULL,NULL );//初始化主协程
	self->cIsMain = 1;//是主协程

	env->pending_co = NULL;
	env->occupy_co = NULL;

	coctx_init( &self->ctx );//初始化ctx,全为0 和coctx_make作用不同

	env->pCallStack[ env->iCallStackSize++ ] = self;//入栈

	stCoEpoll_t *ev = AllocEpoll();//分配内存
	SetEpoll( env,ev );//绑定env和管理时间的ev
}
stCoRoutineEnv_t *co_get_curr_thread_env()//获取env
{
	return gCoEnvPerThread;
}

void OnPollProcessEvent( stTimeoutItem_t * ap )//回调函数,用来启用协程。 poll相关的还有timeout相关的。
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );//重新启动协程
}

void OnPollPreparePfn( stTimeoutItem_t * ap,struct epoll_event &e,stTimeoutItemLink_t *active )
{
	stPollItem_t *lp = (stPollItem_t *)ap;
	lp->pSelf->revents = EpollEvent2Poll( e.events );//更改格式并赋值


	stPoll_t *pPoll = lp->pPoll;
	pPoll->iRaiseCnt++;//计数,大概可以统计epoll的事件

	if( !pPoll->iAllEventDetach )//如果时间没从timeout分离,分离
	{
		pPoll->iAllEventDetach = 1;

		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( pPoll );//移除事件

		AddTail( active,pPoll );//加到active事件中,即事件已经触发

	}
}//将epoll_wait返回的事件格式改为poll,从timeout中删除。并加入active中。


void co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg )//核心 循环处理事件
{
	if( !ctx->result )
	{
		ctx->result =  co_epoll_res_alloc( stCoEpoll_t::_EPOLL_SIZE );
	}//如果result没有分配内存,分配内存。
	co_epoll_res *result = ctx->result;


	for(;;)
	{
		int ret = co_epoll_wait( ctx->iEpollFd,result,stCoEpoll_t::_EPOLL_SIZE, 1 );//不断epoll_wait等待事件触发,用epoll_wait返回的事件,可能触发了,但是时间未到,下面会做处理。

		stTimeoutItemLink_t *active = (ctx->pstActiveList);//已经触发的事件
		stTimeoutItemLink_t *timeout = (ctx->pstTimeoutList);//超时事件

		memset( timeout,0,sizeof(stTimeoutItemLink_t) );//初始化为0

		for(int i=0;i<ret;i++)//把所有的触发了的事件 添加到 已经触发的事件中
		{
			stTimeoutItem_t *item = (stTimeoutItem_t*)result->events[i].data.ptr;
			if( item->pfnPrepare )
			{
				item->pfnPrepare( item,result->events[i],active );//将epoll_wait返回的事件格式改为poll,从timeoutlink中删除。并加入active中。
			}
			else
			{
				AddTail( active,item );
			}//使用co_poll的事件才会初始化pfnPrepare,使用co_cond_timedwait加入的事件不会初始化,co_cond_signal的时候移除事件。
		}


		unsigned long long now = GetTickMS();//获取时间
		TakeAllTimeout( ctx->pTimeout,now,timeout );//获取所有已经超时的事件,从ptimeout中加入到timeout中

		stTimeoutItem_t *lp = timeout->head;
		while( lp )
		{
			//printf("raise timeout %p\n",lp);
			lp->bTimeout = true;
			lp = lp->pNext;
		}//设置超时事件的btimeout属性

		Join<stTimeoutItem_t,stTimeoutItemLink_t>( active,timeout );//把所有timeout事件加入到active中

		lp = active->head;
		while( lp )//遍历所有已经超时的事件
		{

			PopHead<stTimeoutItem_t,stTimeoutItemLink_t>( active );//active出栈
            if (lp->bTimeout && now < lp->ullExpireTime)//如果触发了但是时间没到,加入到ptimeout中
			{
				int ret = AddTimeout(ctx->pTimeout, lp, now);
				if (!ret)
				{
					lp->bTimeout = false;
					lp = active->head;
					continue;
				}
			}
			if( lp->pfnProcess )//时间到了,恢复协程。
			{
				lp->pfnProcess( lp );
			}

			lp = active->head;//下一个触发的事件
		}
		if( pfn )//传入的参数,可以用来退出主协程。
		{
			if( -1 == pfn( arg ) )
			{
				break;
			}
		}

	}
}
void OnCoroutineEvent( stTimeoutItem_t * ap )//恢复协程的,但是好像未使用,上面恢复协程的使用另一个函数。
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );
}


stCoEpoll_t *AllocEpoll()//分配内存
{
	stCoEpoll_t *ctx = (stCoEpoll_t*)calloc( 1,sizeof(stCoEpoll_t) );

	ctx->iEpollFd = co_epoll_create( stCoEpoll_t::_EPOLL_SIZE );//10240个
	ctx->pTimeout = AllocTimeout( 60 * 1000 );//60×1000个link。

	ctx->pstActiveList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) );
	ctx->pstTimeoutList = (stTimeoutItemLink_t*)calloc( 1,sizeof(stTimeoutItemLink_t) );


	return ctx;
}

void FreeEpoll( stCoEpoll_t *ctx )//释放内存
{
	if( ctx )
	{
		free( ctx->pstActiveList );
		free( ctx->pstTimeoutList );
		FreeTimeout( ctx->pTimeout );
		co_epoll_res_free( ctx->result );
	}
	free( ctx );
}

stCoRoutine_t *GetCurrCo( stCoRoutineEnv_t *env )//获取当前协程
{
	return env->pCallStack[ env->iCallStackSize - 1 ];
}
stCoRoutine_t *GetCurrThreadCo( )//获取当前协程
{
	stCoRoutineEnv_t *env = co_get_curr_thread_env();
	if( !env ) return 0;
	return GetCurrCo(env);
}



typedef int (*poll_pfn_t)(struct pollfd fds[], nfds_t nfds, int timeout);
int co_poll_inner( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout, poll_pfn_t pollfunc)
{//pollfunc在hook的poll中调用co_poll_inner时传入co_poll(真的poll
    if (timeout == 0)//timeout为0 直接调用函数
	{
		return pollfunc(fds, nfds, timeout);
	}
	if (timeout < 0)//timeout小于0,时间设为无穷大 调用AddTimeout时会设为size-1。
	{
		timeout = INT_MAX;
	}
	int epfd = ctx->iEpollFd;
	stCoRoutine_t* self = co_self();//当前协程

	//1.struct change
	stPoll_t& arg = *((stPoll_t*)malloc(sizeof(stPoll_t)));
	memset( &arg,0,sizeof(arg) );//新建一个stpoll_t

	arg.iEpollFd = epfd;
	arg.fds = (pollfd*)calloc(nfds, sizeof(pollfd));
	arg.nfds = nfds;//初始化

	stPollItem_t arr[2];
	if( nfds < sizeof(arr) / sizeof(arr[0]) && !self->cIsShareStack)
	{
		arg.pPollItems = arr;
	}//如果不是共享栈且nfds小于2,使用分配的空间
	else
	{
		arg.pPollItems = (stPollItem_t*)malloc( nfds * sizeof( stPollItem_t ) );
	}//用malloc分配空间
	memset( arg.pPollItems,0,nfds * sizeof(stPollItem_t) );//初始化为0

	arg.pfnProcess = OnPollProcessEvent;//重新启动协程的函数
	arg.pArg = GetCurrCo( co_get_curr_thread_env() );//设置env


	//2. add epoll
	for(nfds_t i=0;i<nfds;i++)//添加所有事件到epoll中
	{
		arg.pPollItems[i].pSelf = arg.fds + i;//设置fd
		arg.pPollItems[i].pPoll = &arg;//设置管理的stPoll_t

		arg.pPollItems[i].pfnPrepare = OnPollPreparePfn;//设置函数,分离timeout,添加到active
		struct epoll_event &ev = arg.pPollItems[i].stEvent;//设置的参数

		if( fds[i].fd > -1 )
		{
			ev.data.ptr = arg.pPollItems + i;
			ev.events = PollEvent2Epoll( fds[i].events );//初始化env

			int ret = co_epoll_ctl( epfd,EPOLL_CTL_ADD, fds[i].fd, &ev );//事件加入epoll
			if (ret < 0 && errno == EPERM && nfds == 1 && pollfunc != NULL)//如果epoll加入失败
			{
				if( arg.pPollItems != arr )
				{
					free( arg.pPollItems );
					arg.pPollItems = NULL;
				}
				free(arg.fds);
				free(&arg);
				return pollfunc(fds, nfds, timeout);//用poll加入 用系统的poll 这里没有完善后面的处理 
			}
		}
		//if fail,the timeout would work
	}

	//3.add timeout

	unsigned long long now = GetTickMS();//获取时间
	arg.ullExpireTime = now + timeout;//设置超时时间
	int ret = AddTimeout( ctx->pTimeout,&arg,now );//事件添加到ptimeout中,用来回调
	int iRaiseCnt = 0;
	if( ret != 0 )
	{
		co_log_err("CO_ERR: AddTimeout ret %d now %lld timeout %d arg.ullExpireTime %lld",
				ret,now,timeout,arg.ullExpireTime);
		errno = EINVAL;
		iRaiseCnt = -1;

	}
    else
	{
		co_yield_env( co_get_curr_thread_env() );//让出协程,事件触发之后会回调
		iRaiseCnt = arg.iRaiseCnt;//可以统计有多少事件触发,猜测的0-0
	}

    {
		//clear epoll status and memory
		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &arg );//移除之前加入到ptimeout的事件
		for(nfds_t i = 0;i < nfds;i++)
		{
			int fd = fds[i].fd;
			if( fd > -1 )
			{
				co_epoll_ctl( epfd,EPOLL_CTL_DEL,fd,&arg.pPollItems[i].stEvent );//从epoll中删除
			}
			fds[i].revents = arg.fds[i].revents;
		}


		if( arg.pPollItems != arr )//如果大于两个,释放临时空间
		{
			free( arg.pPollItems );
			arg.pPollItems = NULL;
		}

		free(arg.fds);
		free(&arg);
	}

	return iRaiseCnt;
}

int	co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms )//功能看co_poll
{
	return co_poll_inner(ctx, fds, nfds, timeout_ms, NULL);
}

void SetEpoll( stCoRoutineEnv_t *env,stCoEpoll_t *ev )//绑定env和ev
{
	env->pEpoll = ev;
}
stCoEpoll_t *co_get_epoll_ct()//获取pepoll
{
	if( !co_get_curr_thread_env() )
	{
		co_init_curr_thread_env();
	}
	return co_get_curr_thread_env()->pEpoll;
}
struct stHookPThreadSpec_t//线程共享数据相关?并无实际使用
{
	stCoRoutine_t *co;
	void *value;

	enum
	{
		size = 1024
	};
};
void *co_getspecific(pthread_key_t key)//获取数据
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( !co || co->cIsMain )//如果是主协程,调用系统的函数
	{
		return pthread_getspecific( key );
	}
	return co->aSpec[ key ].value;//返回对应的value
}
int co_setspecific(pthread_key_t key, const void *value)//设置数据
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( !co || co->cIsMain )
	{
		return pthread_setspecific( key,value );
	}
	co->aSpec[ key ].value = (void*)value;
	return 0;
}



void co_disable_hook_sys()//取消hook
{
	stCoRoutine_t *co = GetCurrThreadCo();
	if( co )
	{
		co->cEnableSysHook = 0;
	}
}
bool co_is_enable_sys_hook()//是否hook
{
	stCoRoutine_t *co = GetCurrThreadCo();
	return ( co && co->cEnableSysHook );
}

stCoRoutine_t *co_self()//返回当前协程
{
	return GetCurrThreadCo();
}

//co cond
struct stCoCond_t;
struct stCoCondItem_t//信号链表
{
	stCoCondItem_t *pPrev;
	stCoCondItem_t *pNext;
	stCoCond_t *pLink;

	stTimeoutItem_t timeout;
};
struct stCoCond_t//存储信号链表的和尾
{
	stCoCondItem_t *head;
	stCoCondItem_t *tail;
};
static void OnSignalProcessEvent( stTimeoutItem_t * ap )//信号事件的回调函数
{
	stCoRoutine_t *co = (stCoRoutine_t*)ap->pArg;
	co_resume( co );
}

stCoCondItem_t *co_cond_pop( stCoCond_t *link );//出栈
int co_cond_signal( stCoCond_t *si )//激活一个信号事件
{
	stCoCondItem_t * sp = co_cond_pop( si );//获得栈顶并出栈
	if( !sp )
	{
		return 0;
	}
	RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &sp->timeout );//从ptimeout中删除

	AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout );//添加到活跃事件中

	return 0;
}
int co_cond_broadcast( stCoCond_t *si )//广播 激活所有信号事件
{
	for(;;)
	{
		stCoCondItem_t * sp = co_cond_pop( si );//获得栈顶并出栈
		if( !sp ) return 0;

		RemoveFromLink<stTimeoutItem_t,stTimeoutItemLink_t>( &sp->timeout );//从ptimeout中删除

		AddTail( co_get_curr_thread_env()->pEpoll->pstActiveList,&sp->timeout );//添加到活跃事件中
	}

	return 0;
}


int co_cond_timedwait( stCoCond_t *link,int ms )//添加一个信号事件
{
	stCoCondItem_t* psi = (stCoCondItem_t*)calloc(1, sizeof(stCoCondItem_t));
	psi->timeout.pArg = GetCurrThreadCo();
	psi->timeout.pfnProcess = OnSignalProcessEvent;//初始化

	if( ms > 0 )
	{
		unsigned long long now = GetTickMS();
		psi->timeout.ullExpireTime = now + ms;

		int ret = AddTimeout( co_get_curr_thread_env()->pEpoll->pTimeout,&psi->timeout,now );//添加到ptimeout
		if( ret != 0 )
		{
			free(psi);
			return ret;
		}
	}
	AddTail( link, psi);//添加到link栈顶

	co_yield_ct();//让出协程


	RemoveFromLink<stCoCondItem_t,stCoCond_t>( psi );//事件触发后删除,继续之前timewait的事件
	free(psi);

	return 0;
}
stCoCond_t *co_cond_alloc()//分配内存
{
	return (stCoCond_t*)calloc( 1,sizeof(stCoCond_t) );
}
int co_cond_free( stCoCond_t * cc )//释放内存
{
	free( cc );
	return 0;
}


stCoCondItem_t *co_cond_pop( stCoCond_t *link )//出栈顶,因为是链表所以只需要head=head->next,以及其他的处理
{
	stCoCondItem_t *p = link->head;
	if( p )
	{
		PopHead<stCoCondItem_t,stCoCond_t>( link );
	}
	return p;
}

个人总结 对于每个结构体所对相关的功能进行一个总结。
struct stCoEpoll_t:时间轮相关的,主要报告ptimeout,timeout,active。ptimeout是时间轮,60×1000个link,时间轮大小为60s,所以使用时定时事件不能大于60s。理论上也是可以自己拓展的。插入事件时,在对应的link上插入事件。需要注意的是,add函数参数的now与真正插入的位置无关,插入的位置是超时时间。timeout超时事件,用来获取epoll_wait函数返回的事件。
获取之后加入到active中。active活跃事件,即已经触发的事件,如果未超时,重新插入到ptimeout中,需要注意的是,每个事件在触发后在其link中都会删除。例如active中事件,会删除,然后加入到ptimeout。
struct stCoRoutineEnv_t :调用栈相关的函数,yield和resume等相关函数都是根据调用栈来切换协程。协程正是通过调用栈和时间轮两个一起管理。
struct stTimeoutItem_t:时间轮里面的使用的结构体,用来管理事件,以及回调函数。还有超时事件,协程等属性。add、takeall,free,alloc。等都是在链表上进行的操作。因为每一个都是存储了管理他的link(存储head,tail)和他的pprev,pnext。所以删除,增删都是极快的。
stPollItem_t :poll都是继承了stTimeoutItem_t,用于epoll事件触发后,用对应的stTimeoutItem_t来启用协程,或者其他操作。
本身是poll属性的,再使用epoll时进行了格式转换。
stCoCond_t:信号相关的事件。结构体的数据类型和上述几个类似,不过这次存储head和tail的没有命名为link。
主要函数有co_cond_timedwait,添加等待事件,初始化之后添加到ptimeout中,并添加初始化的时间到cond的link中,然后让出协程,协程触发后从link中删除。co_cond_signal,移除栈顶的等待事件,从cond的link和ptimeout中删除,并添加到active中。
active会去触发timewait的事件,如果时间未到重新加入ptimeout。co_cond_broadcast 广播就是触发所有的等待事件。过程和
co_cond_signal类似。 cond事件在一种情况下会产生错误日志,timewait设置的事件,已经超时触发了,才有singal来触发事件。这时超时时间小于now,并不会加入到时间轮中。
if( apItem->ullExpireTime < allNow )
{
co_log_err();
return LINE;
}
总体来说libco是一个比较优秀的库,学习过程中会发现很多设计上细节的东西,对于各种情况的处理,文件4K大小,根据大小用不同方式分配内存,等等。虽然有很多东西没放出来,管中窥豹,还是很厉害的。初学者可以学到很多东西。

你可能感兴趣的:(libco)