- 1 数据结构和宏
- 1.1 协程的状态码
- 1.2 协程的执行状态码
- 1.3 lua_State 协程结构体
- 2 C API
- 2.1 lua_newthread 新建一个协程,压栈,返回这个新协程
- 2.2 luaE_freethread 释放一个协程L1
- 2.3 lua_status 获取协程的错误码
- 2.4 lua_resume 唤醒一个协程,nargs为参数个数,返回值为协程状态码
- 2.5 lua_yield 挂起一个协程
- 3 Lua API
- 3.1 协程模块的注册
- 3.1.1 luaL_register(L, LUA_COLIBNAME, co_funcs) 注册协程模块
- 3.1.2 co_funcs 注册的协程库函数列表
- 3.2 luaB_cocreate 创建一个协程,协程留在栈顶
- 3.3 luaB_costatus 获取当前协程的状态,将状态字符串压栈
- 3.4 luaB_coresume 唤醒一个协程,栈顶为[true值,协程yield或return的值] 或者为[false值,,错误信息]
- 3.5 luaB_corunning 获取当前正在执行的协程,压栈
- 3.6 luaB_cowrap 创建一个协程和一个C闭包,并以该协程作为该C闭包的upvalue,将该C闭包压栈
- 3.7 luaB_yield 挂起协程
1 数据结构和宏
1.1 协程的状态码
(lua.h)
#define LUA_YIELD 1 //挂起
#define LUA_ERRRUN 2 //运行时错误
#define LUA_ERRSYNTAX 3 //语法错误
#define LUA_ERRMEM 4 //内存错误
#define LUA_ERRERR 5 //在错误处理函数中出错
1.2 协程的执行状态码
(lbaselib.c)
#define CO_RUN 0 //执行中状态
#define CO_SUS 1 //挂起状态
#define CO_NOR 2 //常规状态(这个协程唤醒了另一个协程)
#define CO_DEAD 3 //死亡状态//协程执行状态码对应的字符串
static const char *const statnames[] ={"running", "suspended", "normal", "dead"};
1.3 lua_State 协程结构体
(lstate.h) lua_State
struct lua_State
{CommonHeader;//#define CommonHeader GCObject *next; lu_byte tt; lu_byte markedlu_byte status;//协程的状态码StkId top;//栈顶位置,“寄存器”第一个可用位置StkId base;//当前函数调用的栈基址global_State* l_G;//全局状态机CallInfo* ci;//当前函数调用信息const Instruction* savedpc;//指令指针StkId stack_last;//“寄存器”最后一个可用位置StkId stack;//栈数组的起始位置CallInfo* end_ci;//函数调用信息数组的最后一个位置的下一个位置CallInfo* base_ci;//函数调用信息数组首地址int stacksize;//栈的大小int size_ci;//函数调用信息数组大小unsigned short nCcalls;//内嵌C调用的层数unsigned short baseCcalls;//唤醒协程时的内嵌C调用层数lu_byte hookmask;lu_byte allowhook;int basehookcount;int hookcount;lua_Hook hook;TValue l_gt;//Global表TValue env;//环境表的临时位置GCObject* openupval;//栈上open状态的uvaluesGCObject* gclist;struct lua_longjmp* errorJmp;//当前跳转信息,实现try catch的关键结构ptrdiff_t errfunc;//当前错误处理函数相对于“寄存器数组首地址”的偏移地址
};
2 C API
2.1 lua_newthread 新建一个协程,压栈,返回这个新协程
(lapi.c) lua_newthread
LUA_API lua_State* lua_newthread(lua_State* L)
{luaC_checkGC(L);lua_State* L1 = luaE_newthread(L);setthvalue(L, L->top, L1);api_incr_top(L);//#define api_incr_top(L) { L->top++; }return L1;
}
(lstate.c) luaE_newthread 新建一个协程并返回这个协程
lua_State* luaE_newthread(lua_State* L)
{//#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))//#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)lua_State* L1 = tostate(luaM_malloc(L, state_size(lua_State)));luaC_link(L, obj2gco(L1), LUA_TTHREAD);//初始化协程preinit_state(L1, G(L));//栈初始化(初始化 CallInfo 数组,“寄存器” 数组,第一个CallInfo)stack_init(L1, L);//所有的协程的Global表都是用的主协程的Global表哦setobj2n(L, gt(L1), gt(L));L1->hookmask = L->hookmask;L1->basehookcount = L->basehookcount;L1->hook = L->hook;resethookcount(L1);//#define resethookcount(L) (L->hookcount = L->basehookcount)return L1;
}
2.2 luaE_freethread 释放一个协程L1
(lstate.c)
void luaE_freethread(lua_State* L, lua_State* L1)
{//关闭协程L1上所有upvalueluaF_close(L1, L1->stack);//释放协程L1上“寄存器”数组和CallInfo数组freestack(L, L1);//释放协程L1本身的内存//#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)//#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)//#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)luaM_freemem(L, fromstate(L1), state_size(lua_State));
}
2.3 lua_status 获取协程的错误码
(lapi.c) lua_status
LUA_API int lua_status(lua_State* L)
{return L->status;
}
2.4 lua_resume 唤醒一个协程,nargs为参数个数,返回值为协程状态码
(ldo.c) lua_resume
LUA_API int lua_resume(lua_State* L, int nargs)
{if (L->status != LUA_YEILD && (L->status != 0 || L->ci != L->base_ci)){return resume_error(L, "cannot resume non-suspend coroutine");}if (L->nCcalls >= LUAI_MAXCCALLS){return resume_error(L, "C stack overflow");}L->baseCcalls = ++L->nCcalls;int status = luaD_rawrunprotected(L, resume, L->top - nargs);if (status != 0){//记录状态码L->status = cast_byte(status);//根据状态码status将错误入栈luaD_seterrorobj(L, status, L->top);L->ci->top = L->top;}else{status = L->status;}--L->nCcalls;return status;
}
(ldo.c) resume_error 将错误信息msg压栈
static int resume_error(lua_State* L, const char* msg)
{L->top = L->ci->top;setsvalue2s(L, L->top, luaS_new(L, msg));incr_top(L);return LUA_ERRRUN;
}
(ldo.c) resume 唤醒协程
static void resume(lua_State* L, void* ud)
{StkId firstArg = cast(StkId, ud);CallInfo* ci = L->ci;//若状态码为0,则执行函数if (L->status == 0){//若是C函数,直接结束,因为若是C函数那么在luaD_precall里面就执行过了if (luaD_precall(L, fisrtArg - 1, LUA_MULTRET) != PCRLUA){return;}}else{L->status = 0;//若是C函数//#define ci_func(ci) (clvalue((ci)->func))//#define f_isLua(ci) (!ci_func(ci)->c.isC)if (!f_isLua(ci)){//函数收尾工作。若函数实际返回值数量!=函数原型返回值数量,则调整栈顶if (luaD_poscall(L, firstArg)){L->top = L->ci->top;}}//若是lua函数,恢复函数调用栈基址else{L->base = L->ci->base;}}//继续执行指令luaV_execute(L, cast_int(L->ci - L->base_ci));
}
2.5 lua_yield 挂起一个协程
(ldo.c) lua_yield
LUA_API int lua_yield(lua_State* L, int nresults)
{if (L->nCcalls > L->baseCcalls){luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");}//调整函数调用的栈基址L->base = L->top - nresults;//标记协程状态为LUA_YIELDL->status = LUA_YIELD;return -1;
}
3 Lua API
3.1 协程模块的注册
3.1.1 luaL_register(L, LUA_COLIBNAME, co_funcs) 注册协程模块
(lbaselib.c) luaopen_base
LUALIB_API int luaopen_base(lua_State* L)
{base_open(L);//base_open见模块章节//注册协程模块,LUA_COLIBNAME为模块名,co_funcs为库函数列表//#define LUA_COLIBNAME "coroutine"luaL_register(L, LUA_COLIBNAME, co_funcs);//luaL_register见模块章节return 2;
}
3.1.2 co_funcs 注册的协程库函数列表
(lbaselib.c)
static const luaL_Reg co_funcs[] = {{"create", luaB_cocreate},{"resume", luaB_coresume},{"running", luaB_corunning},{"status", luaB_costatus},{"wrap", luaB_cowrap},{"yield", luaB_yield},{NULL, NULL}
};
3.2 luaB_cocreate 创建一个协程,协程留在栈顶
(lbaselib.c) luaB_cocreate
static int luaB_cocreate(lua_State* L)
{//新建一个协程NLlua_State* NL = lua_newthread(L);//将函数移到栈顶(前提:函数已经就是第一个参数了)lua_pushvalue(L, 1);//将函数从协程L移到协程NLlua_xmove(L, NL, 1);return 1;
}
3.3 luaB_costatus 获取当前协程的状态,将状态字符串压栈
(lbaselib.c) luaB_costatus
static int luaB_costatus(lua_State* L)
{lua_State* co = lua_tothread(L, 1);lua_pushstring(L, statnames[costatus(L, co)]);return 1;
}
(lbaselib.c) costatus 获取协程的状态码
static int costatus(lua_State* L, lua_State* co)
{//若L==co,必定是 执行中状态if (L == co){return CO_RUN;}//根据co的状态码,来决定其运行状态switch (lua_status(co)){//挂起状态case LUA_YIELD:{return CO_SUS;}//若为0,看情况case 0:{lua_Debug ar;//若还有栈帧,则说明是normal状态if (lua_getstack(co, 0, &ar) > 0){return CO_NOR;}//否则,若无参数,则说明协程已死else if (lua_gettop(co) == 0){return CO_DEAD;}//其余情况,则为挂起状态else{return CO_SUS;}}//其余情况则为死亡状态default:{return CO_DEAD;}}
}
(ldebug.c) lua_getstack 获取第level层函数调用的栈的状态
LUA_API int lua_getstack(lua_Stack* L, int level, lua_Debug* ar)
{//寻找 满足 level==0 或者 为base_ci 的 CallInfofor (CallInfo* ci = L->ci; level > 0 && ci > L->base_ci; ci--){level--;//#define ci_func(ci) (clvalue((ci)->func))//#define f_isLua(ci) (!ci_func(ci)->c.isC)if (f_isLua(ci)){level -= ci->tailcalls;}}if (level == 0 && ci > L->base_ci){status = 1;ar->i_ci = cast_int(ci -> L->base_ci);}else if (level < 0){status = 1;ar->i_ci = 0;}//ci == L->base_cielse{status = 0;}return status;
}
3.4 luaB_coresume 唤醒一个协程,栈顶为[true值,协程yield或return的值] 或者为[false值,,错误信息]
(lbaselib.c) luaB_coresume
static int luaB_coresume(lua_State* L)
{//协程为第一个参数,传给协程的参数个数为lua_gettop(L) - 1lua_State* co = lua_tothread(L, 1);//唤醒协程,r若<0则表示出错,r>=0表示 协程yield或return的值的数量int r = auxresume(L, co, lua_gettop(L) - 1);if (r < 0){//错误信息在栈顶,false值插入错误信息下lua_pushboolean(L, 0);lua_insert(L, -2);return 2;}else{//true值插入r个返回值之下lua_pushboolean(L, 1);lua_insert(L, -(r + 1));return r + 1;}
}
(lbaselib.c) auxresume 恢复协程co,参数narg个,返回值为 协程yield或return的值的数量
static int auxresume(lua_State* L, lua_State* co, int narg)
{int status = costatus(L, co);//若栈无 需要参数个数 的空闲空间,且扩容失败,则报错if (!lua_checkstack(co, narg)){luaL_error(L, "too many arguments to resume");}//co不是挂起状态,则报错if (status != CO_SUS){lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);return -1;}//将协程L栈顶的narg个值出栈,移到协程co的栈顶lua_xmove(L, co, narg);//将协程L的C调用层数 赋值给 协程co的C调用层数lua_setlevel(L, co);//co->nCcalls = L->nCcalls;status = lua_resume(co, narg);if (status == 0 || status == LUA_YIELD){int nres = lua_gettop(co);if (!lua_checkstack(L, nres + 1)){luaL_error(L, "too many results to resume");}//将协程co yeild的返回值压入 协程L的栈顶lua_xmove(co, L, nres);return nres;}else{//将栈顶的错误信息从协程co移到协程Llua_xmove(co, L, 1);return -1;}
}
3.5 luaB_corunning 获取当前正在执行的协程,压栈
(lbaselib.c) luaB_corunning
static int luaB_corunning(lua_State* L)
{//将L放在L的栈顶if (lua_pushthread(L))//若 G(L)->mainthread == L 为true{lua_pushnil(L);//若为主协程,则返回nil。因为lua用的不对称协程,所以主协程不能当作一般的协程看待。}return 1;
}
3.6 luaB_cowrap 创建一个协程和一个C闭包,并以该协程作为该C闭包的upvalue,将该C闭包压栈
(lbaselib.c) luaB_cowrap
static int luaB_cowrap(lua_State* L)
{luaB_cocreate(L);lua_pushcclosure(L, luaB_auxwrap, 1);return 1;
}
(lbaselib.c) luaB_auxwrap 协程的包装函数
static int luaB_auxwrap(lua_State* L)
{lua_State* co = lua_tothread(L, lua_upvalueindex(1));//唤醒协程int r = auxresume(L, co, lua_gettop(L));if (r < 0){//若栈顶是字符串类型,则栈顶的为错误信息if (lua_isstring(L, -1)){luaL_where(L, 1);//获取堆栈信息lua_insert(L, -2);//插入错误信息之前lua_concat(L, 2);//连接错误信息}lua_error(L);//传递错误信息}return r;
}
3.7 luaB_yield 挂起协程
(lbaselib.c) luaB_yield
static int luaB_yield(lua_State* L)
{return lua_yield(L, lua_gettop(L));
}