由於本源代碼蠻長的,所以按照功能劃分模塊來分析,分爲若干部分,詳見二級目錄↑
代碼文件:co_routine.h,co_routine.cpp,co_routine_inner.h
三、協程的執行
void co_yield_env( stCoRoutineEnv_t *env );//將當前執行的env從協程棧中出棧並將執行權交給父協程。
void co_yield_env( stCoRoutineEnv_t *env )
{
stCoRoutine_t *last = env->pCallStack[ env->iCallStackSize - 2 ];
stCoRoutine_t *curr = env->pCallStack[ env->iCallStackSize - 1 ];
env->iCallStackSize--;
co_swap( curr, last);
}
//切出到主協程
void co_yield_ct()
{
co_yield_env( co_get_curr_thread_env() );
}
//跟co_yield_env功能一樣只是參數不同。
void co_yield( stCoRoutine_t *co )
{
co_yield_env( co->env );
}
static int CoRoutineFunc( stCoRoutine_t *co,void * );函數內調用了pfn函數(pfn函數就是協程的主函數)
執行完畢後cEnd標記爲1
這裏修改了子協程的env就完成了出棧工作,說明,env是父子協程共享的。
static int CoRoutineFunc( stCoRoutine_t *co,void * )
{
if( co->pfn )
{
co->pfn( co->arg );//執行主函數,內部允許嵌套有限數量的協程調用
}
co->cEnd = 1;//執行完畢後標記爲1
stCoRoutineEnv_t *env = co->env;//env是指針,修改後co的原內容也同步改變。其實下面可以直接寫co->env,因爲下面的函數沒有改變env指針值。
co_yield_env( env );//將當前執行的co從協程棧中出棧並將執行權交給父協程。
return 0;
}
void co_free( stCoRoutine_t *co );//負責協程銷燬工作。主要是提前銷燬用戶棧。推測這裏的用戶棧不能是從share棧上摘下來的。
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但是不銷燬用戶棧,推測是因爲這裏的用戶棧是從share棧上摘下來的,所以不需要歸還os還可以重複利用。
void co_release( stCoRoutine_t *co )//銷燬co但是不銷燬用戶棧
{
co_free( co );
}
void save_stack_buffer(stCoRoutine_t* occupy_co);//用於備份用戶棧有效數據。
void save_stack_buffer(stCoRoutine_t* occupy_co)
{
///copy out
stStackMem_t* stack_mem = occupy_co->stack_mem;//取用戶棧
int len = stack_mem->stack_bp - occupy_co->stack_sp;//計算有效數據長度
if (occupy_co->save_buffer)
{
free(occupy_co->save_buffer), occupy_co->save_buffer = NULL;
}
occupy_co->save_buffer = (char*)malloc(len); //malloc buf;
occupy_co->save_size = len;
memcpy(occupy_co->save_buffer, occupy_co->stack_sp, len);
}
co_swap,沒看懂,佔坑待補
在一些參考博客fork的代碼中,發現沒有此函數(一些occupy之類的東西也沒有),所以這個函數可能是是官方後來更新上去的。其功能先認爲跟coctx_swap相同吧(學習要連猜帶蒙......)。
void co_swap(stCoRoutine_t* curr, stCoRoutine_t* pending_co)
{
stCoRoutineEnv_t* env = co_get_curr_thread_env();
//get curr stack sp
char c;
curr->stack_sp= &c;
if (!pending_co->cIsShareStack)
{
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;
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);
}
}
}