co_routine.cpp分析

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:信號相關的事件。待更

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章