co_routine.cpp
#include "co_routine.h"
#include "co_routine_inner.h"
#include "co_epoll.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <map>
#include <poll.h>
#include <sys/time.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <limits.h>
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
{
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:信號相關的事件。待更