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。從代碼上來說吧:

 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的定義如下:

 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 5.2中調用路徑包含c函數的時候也能夠進行yield,只不過不甚好看。由於c函數不能保存堆棧,所以lua的策略是直接放棄當前c函數的棧幀,而讓調用者本身提供一個continuation,當resume時調用上面被無視的uion里的c.k。沒用過,所以也不深入考究了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM