Skip to content

Instantly share code, notes, and snippets.

@losophy
Last active August 5, 2021 01:45
Show Gist options
  • Save losophy/f3db9c6959ce9c472719e3798b0b0baa to your computer and use it in GitHub Desktop.
Save losophy/f3db9c6959ce9c472719e3798b0b0baa to your computer and use it in GitHub Desktop.
lua-5.1.1中的协程的实现
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;
}

lthread

这是从lua-5.1.1中分离出来的协程实现代码。

关于lua协程

lua协程

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment