lua協程實現簡析

協程,簡單來講就是新建立一個協助程序(co = coroutine.create(func)),而後須要手動去啓動它(coroutine.resume(co)),在它最終退出以前,它有可能暫停屢次返回階段性的結果(coroutine.yield(co)),每一次暫停以後都必須手動去恢復它(coroutine.resume(co))。數組

協程在lua源文件中對應lcorolib.c,數組co_funcs中定義了c暴露給lua的接口。從上面的描述看和c函數調用有點類似,只不過c函數只有一個出口,因此不可能返回屢次。題外話,爲何c函數只有一個出口?我本身粗淺的理解是由於c函數的全部信息都放在棧上,而c語言沒有提供原生的保存/恢復棧空間的支持,因此沒有中途退出後還能生新進入這個概念。實際上,協程和系統級別的進程切換更像一點,都是保存堆棧,而後恢復。我想最大的不一樣就是協程知道接下來的控制權在哪裏,而進程不知道。根本上它們想實現的功能就不同吧。函數

好了,那協程實現的要點就是堆棧的保存與恢復了。固然,這裏的堆棧不是進程自己的堆棧,而是lua的soft stack。從代碼上來講吧:ui

 82 static int luaB_cocreate (lua_State *L) {
 83   lua_State *NL;
 84   luaL_checktype(L, 1, LUA_TFUNCTION);
 85   NL = lua_newthread(L);
 86   lua_pushvalue(L, 1);  /* move function to top */
 87   lua_xmove(L, NL, 1);  /* move function from L to NL */
 88   return 1;
 89 }

其中NL就是新建立的協程的棧,之後全部的保存/恢復都是針對這個棧。lua_State這個結構體裏對協程實現最重要的是CallInfo *ci,CallInfo的定義以下:this

 66 /*
 67 ** information about a call
 68 */
 69 typedef struct CallInfo {
 70   StkId func;  /* function index in the stack */
 71   StkId top;  /* top for this function */
 72   struct CallInfo *previous, *next;  /* dynamic call link */
 73   short nresults;  /* expected number of results from this function */
 74   lu_byte callstatus;
 75   ptrdiff_t extra;
 76   union {
 77     struct {  /* only for Lua functions */
 78       StkId base;  /* base for this function */
 79       const Instruction *savedpc;
 80     } l;
 81     struct {  /* only for C functions */
 82       int ctx;  /* context info. in case of yields */
 83       lua_CFunction k;  /* continuation in case of yields */
 84       ptrdiff_t old_errfunc;
 85       lu_byte old_allowhook;
 86       lu_byte status;
 87     } c;
 88   } u;
 89 } CallInfo;

其中func指向當前調用的函數在棧上的位置,而savedpc就是保存的指令執行位置(先無視union裏的c),根據這兩個值就能恢復函數的執行點。然而在yield的時候真正負責保存函數位置的是extra(保存func與棧頂的相對位置),在resume時func會根據extra來恢復,有沒有這個須要我是表示懷疑的,由於就算resume傳遞的參數致使棧realloc,使func失效,但在luaD_reallocstack內會調用correctstack將調用鏈上全部的func從新設置爲正確的值,因此這裏是否是多餘的呢?lua

在lua 5.2中調用路徑包含c函數的時候也可以進行yield,只不過不甚好看。因爲c函數不能保存堆棧,因此lua的策略是直接放棄當前c函數的棧幀,而讓調用者自己提供一個continuation,當resume時調用上面被無視的uion裏的c.k。沒用過,因此也不深刻考究了。spa

相關文章
相關標籤/搜索