这是从lua-5.1.1中分离出来的协程实现代码。
Last active
August 5, 2021 01:45
-
-
Save losophy/f3db9c6959ce9c472719e3798b0b0baa to your computer and use it in GitHub Desktop.
lua-5.1.1中的协程的实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
LUA_API lua_State *lua_newthread (lua_State *L) { | |
lua_State *L1; | |
lua_lock(L); | |
luaC_checkGC(L); | |
L1 = luaE_newthread(L); | |
setthvalue(L, L->top, L1); | |
api_incr_top(L); | |
lua_unlock(L); | |
luai_userstatethread(L, L1); | |
return L1; | |
} | |
lua_State *luaE_newthread (lua_State *L) { | |
lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); | |
luaC_link(L, obj2gco(L1), LUA_TTHREAD); | |
preinit_state(L1, G(L)); | |
stack_init(L1, L); /* init stack */ | |
setobj2n(L, gt(L1), gt(L)); /* share table of globals */ | |
L1->hookmask = L->hookmask; | |
L1->basehookcount = L->basehookcount; | |
L1->hook = L->hook; | |
resethookcount(L1); | |
lua_assert(iswhite(obj2gco(L1))); | |
return L1; | |
} | |
LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { | |
int i; | |
if (from == to) return; | |
lua_lock(to); | |
api_checknelems(from, n); | |
api_check(from, G(from) == G(to)); | |
api_check(from, to->ci->top - to->top >= n); | |
from->top -= n; | |
for (i = 0; i < n; i++) { | |
setobj2s(to, to->top++, from->top + i); | |
} | |
lua_unlock(to); | |
} | |
static int luaB_cocreate (lua_State *L) { | |
lua_State *NL = lua_newthread(L);//调用lua newthread创建lua State结构体 | |
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,//检查当前栈顶的元素是不是一个函数对象,因为需要一个函数作为协程开始运行时的主函数。 这个主函数必须是Lua函数, C函数将会报错 | |
"Lua function expected"); | |
lua_pushvalue(L, 1); /* move function to top *///将协程主函数压入当前 lua State 的栈中 | |
lua_xmove(L, NL, 1); /* move function from L to NL *///调用 lua xmove 将该函数从当前的lua State移动到新创建的协程的 lua State栈中 | |
return 1; | |
} | |
static int luaB_coresume (lua_State *L) { | |
lua_State *co = lua_tothread(L, 1); | |
int r; | |
luaL_argcheck(L, co, 1, "coroutine expected");//检查当前栈顶元素是不是协程指针 | |
r = auxresume(L, co, lua_gettop(L) - 1);//调用辅助函数auxresume进行实际的resume操作 | |
if (r < 0) {//根据auxresume的返回值来做不同的处理。 当返回值小于0时,说明 resume操作出错,并且此时出错信息在栈顶,因此压入false以及出错消息 | |
lua_pushboolean(L, 0); | |
lua_insert(L, -2); | |
return 2; /* return false + error message */ | |
} | |
else {//否则, auxresume的返回值表示执行resume操作时返回的参数数量,这种情况下压入true以及这些返回参数 | |
lua_pushboolean(L, 1); | |
lua_insert(L, -(r + 1)); | |
return r + 1; /* return true + `resume' returns */ | |
} | |
} | |
static int auxresume (lua_State *L, lua_State *co, int narg) { | |
int status; | |
if (!lua_checkstack(co, narg))//检查数据的合法性 | |
luaL_error(L, "too many arguments to resume"); | |
if (lua_status(co) == 0 && lua_gettop(co) == 0) { | |
lua_pushliteral(L, "cannot resume dead coroutine"); | |
return -1; /* error flag */ | |
} | |
lua_xmove(L, co, narg);//将参数通过lua_xmove函数传递到待启动的协程中 | |
status = lua_resume(co, narg);//调用 lua_resume函数执行协程代码 | |
if (status == 0 || status == LUA_YIELD) { | |
int nres = lua_gettop(co); | |
if (!lua_checkstack(L, nres)) | |
luaL_error(L, "too many results to resume"); | |
lua_xmove(co, L, nres); /* move yielded values */// 当 lua_resume函数返回时,说明该协程已经执行完毕,通过lua_xmove函数将yield传入的参数传递回启动该协程的协程 | |
return nres; | |
} | |
else { | |
lua_xmove(co, L, 1); /* move error message */ | |
return -1; /* error flag */ | |
} | |
} | |
static void resume (lua_State *L, void *ud) { | |
StkId firstArg = cast(StkId, ud); | |
CallInfo *ci = L->ci; | |
if (L->status == 0) { /* start coroutine? *///如果当前协程的状态是0 ,那么说明它是第一次执行 resume操作 | |
lua_assert(ci == L->base_ci && firstArg > L->base); | |
if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)//调用 luaD_precall做函数调用前的准备工作 | |
return;//如果luaD_precall函数的返回值不是PCRLUA ,说明是在C函数中进行resume操作的,此时并不需要后面的 luaV execute函数,就直接返回了 | |
} | |
else { /* resuming from previous yield *///否则就从之前的 YIELD状态中继续执行 | |
lua_assert(L->status == LUA_YIELD); | |
L->status = 0;//首先将协程的状态置为0 | |
if (!f_isLua(ci)) { /* `common' yield? *///判断此时ci的类型 | |
/* finish interrupted execution of `OP_CALL' */ | |
lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || | |
GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); | |
if (luaD_poscall(L, firstArg)) /* complete it... *///如果不是Lua函数,说明之前是被中断的函数调用,此时调用luaD_poscall函数继续完成未完的函数操作 | |
L->top = L->ci->top; /* and correct top if not multiple results */ | |
} | |
else /* yielded inside a hook: just continue its execution */ | |
L->base = L->ci->base;//否则只需要调整 base指针指向之前的ci的base指针即可 | |
} | |
luaV_execute(L, cast_int(L->ci - L->base_ci));//以上的几种情况最终都会调用 luaV_execute 函数来进入 Lua虚拟机中执行 。 这里可以看到,由于使用了同样的结构lua State来表示Lua虚拟机和Lua协程,在表达Lua虚拟机的执行和协程的执行上,两者都是统一使用 luaV execute函数,方便了实现。 | |
} | |
LUA_API int lua_yield (lua_State *L, int nresults) { | |
luai_userstateyield(L, nresults); | |
lua_lock(L); | |
if (L->nCcalls > 0) | |
luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); | |
L->base = L->top - nresults; /* protect stack slots below */ | |
L->status = LUA_YIELD; | |
lua_unlock(L); | |
return -1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment