#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大小,根据大小用不同方式分配内存,等等。虽然有很多东西没放出来,管中窥豹,还是很厉害的。初学者可以学到很多东西。