/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Lua │ │ Copyright © 2004-2023 Lua.org, PUC-Rio. │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files (the │ │ "Software"), to deal in the Software without restriction, including │ │ without limitation the rights to use, copy, modify, merge, publish, │ │ distribute, sublicense, and/or sell copies of the Software, and to │ │ permit persons to whom the Software is furnished to do so, subject to │ │ the following conditions: │ │ │ │ The above copyright notice and this permission notice shall be │ │ included in all copies or substantial portions of the Software. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ │ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ │ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ │ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ │ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ │ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ #define lcorolib_c #define LUA_LIB #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" __static_yoink("lua_notice"); static lua_State *getco (lua_State *L) { lua_State *co = lua_tothread(L, 1); luaL_argexpected(L, co, 1, "thread"); return co; } /* ** Resumes a coroutine. Returns the number of results for non-error ** cases or -1 for errors. */ static int auxresume (lua_State *L, lua_State *co, int narg) { int status, nres; if (l_unlikely(!lua_checkstack(co, narg))) { lua_pushliteral(L, "too many arguments to resume"); return -1; /* error flag */ } lua_xmove(L, co, narg); status = lua_resume(co, L, narg, &nres); if (l_likely(status == LUA_OK || status == LUA_YIELD)) { if (l_unlikely(!lua_checkstack(L, nres + 1))) { lua_pop(co, nres); /* remove results anyway */ lua_pushliteral(L, "too many results to resume"); return -1; /* error flag */ } lua_xmove(co, L, nres); /* move yielded values */ return nres; } else { lua_xmove(co, L, 1); /* move error message */ return -1; /* error flag */ } } static int luaB_coresume (lua_State *L) { lua_State *co = getco(L); int r; r = auxresume(L, co, lua_gettop(L) - 1); if (l_unlikely(r < 0)) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ } else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); return r + 1; /* return true + 'resume' returns */ } } 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 (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ stat = lua_closethread(co, L); /* close its tbc variables */ lua_assert(stat != LUA_OK); lua_xmove(co, L, 1); /* move error message to the caller */ } if (stat != LUA_ERRMEM && /* not a memory error and ... */ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ luaL_where(L, 1); /* add extra info, if available */ lua_insert(L, -2); lua_concat(L, 2); } return lua_error(L); /* propagate error */ } return r; } static int luaB_cocreate (lua_State *L) { lua_State *NL; luaL_checktype(L, 1, LUA_TFUNCTION); NL = lua_newthread(L); lua_pushvalue(L, 1); /* move function to top */ lua_xmove(L, NL, 1); /* move function from L to NL */ return 1; } static int luaB_cowrap (lua_State *L) { luaB_cocreate(L); lua_pushcclosure(L, luaB_auxwrap, 1); return 1; } static int luaB_yield (lua_State *L) { return lua_yield(L, lua_gettop(L)); } #define COS_RUN 0 #define COS_DEAD 1 #define COS_YIELD 2 #define COS_NORM 3 static const char *const statname[] = {"running", "dead", "suspended", "normal"}; static int auxstatus (lua_State *L, lua_State *co) { if (L == co) return COS_RUN; else { switch (lua_status(co)) { case LUA_YIELD: return COS_YIELD; case LUA_OK: { lua_Debug ar; if (lua_getstack(co, 0, &ar)) /* does it have frames? */ return COS_NORM; /* it is running */ else if (lua_gettop(co) == 0) return COS_DEAD; else return COS_YIELD; /* initial state */ } default: /* some error occurred */ return COS_DEAD; } } } static int luaB_costatus (lua_State *L) { lua_State *co = getco(L); lua_pushstring(L, statname[auxstatus(L, co)]); return 1; } static int luaB_yieldable (lua_State *L) { lua_State *co = lua_isnone(L, 1) ? L : getco(L); lua_pushboolean(L, lua_isyieldable(co)); return 1; } static int luaB_corunning (lua_State *L) { int ismain = lua_pushthread(L); lua_pushboolean(L, ismain); return 2; } static int luaB_close (lua_State *L) { lua_State *co = getco(L); int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { status = lua_closethread(co, L); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; } else { lua_pushboolean(L, 0); lua_xmove(co, L, 1); /* move error message */ return 2; } } default: /* normal or running coroutine */ return luaL_error(L, "cannot close a %s coroutine", statname[status]); } } static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, {"isyieldable", luaB_yieldable}, {"close", luaB_close}, {NULL, NULL} }; LUAMOD_API int luaopen_coroutine (lua_State *L) { luaL_newlib(L, co_funcs); return 1; }