18e3e3a7aSWarner Losh /* 20495ed39SKyle Evans ** $Id: lcorolib.c $ 38e3e3a7aSWarner Losh ** Coroutine Library 48e3e3a7aSWarner Losh ** See Copyright Notice in lua.h 58e3e3a7aSWarner Losh */ 68e3e3a7aSWarner Losh 78e3e3a7aSWarner Losh #define lcorolib_c 88e3e3a7aSWarner Losh #define LUA_LIB 98e3e3a7aSWarner Losh 108e3e3a7aSWarner Losh #include "lprefix.h" 118e3e3a7aSWarner Losh 128e3e3a7aSWarner Losh 138e3e3a7aSWarner Losh #include <stdlib.h> 148e3e3a7aSWarner Losh 158e3e3a7aSWarner Losh #include "lua.h" 168e3e3a7aSWarner Losh 178e3e3a7aSWarner Losh #include "lauxlib.h" 188e3e3a7aSWarner Losh #include "lualib.h" 198e3e3a7aSWarner Losh 208e3e3a7aSWarner Losh 218e3e3a7aSWarner Losh static lua_State *getco (lua_State *L) { 228e3e3a7aSWarner Losh lua_State *co = lua_tothread(L, 1); 230495ed39SKyle Evans luaL_argexpected(L, co, 1, "thread"); 248e3e3a7aSWarner Losh return co; 258e3e3a7aSWarner Losh } 268e3e3a7aSWarner Losh 278e3e3a7aSWarner Losh 280495ed39SKyle Evans /* 290495ed39SKyle Evans ** Resumes a coroutine. Returns the number of results for non-error 300495ed39SKyle Evans ** cases or -1 for errors. 310495ed39SKyle Evans */ 328e3e3a7aSWarner Losh static int auxresume (lua_State *L, lua_State *co, int narg) { 330495ed39SKyle Evans int status, nres; 34*8c784bb8SWarner Losh if (l_unlikely(!lua_checkstack(co, narg))) { 358e3e3a7aSWarner Losh lua_pushliteral(L, "too many arguments to resume"); 368e3e3a7aSWarner Losh return -1; /* error flag */ 378e3e3a7aSWarner Losh } 388e3e3a7aSWarner Losh lua_xmove(L, co, narg); 390495ed39SKyle Evans status = lua_resume(co, L, narg, &nres); 40*8c784bb8SWarner Losh if (l_likely(status == LUA_OK || status == LUA_YIELD)) { 41*8c784bb8SWarner Losh if (l_unlikely(!lua_checkstack(L, nres + 1))) { 428e3e3a7aSWarner Losh lua_pop(co, nres); /* remove results anyway */ 438e3e3a7aSWarner Losh lua_pushliteral(L, "too many results to resume"); 448e3e3a7aSWarner Losh return -1; /* error flag */ 458e3e3a7aSWarner Losh } 468e3e3a7aSWarner Losh lua_xmove(co, L, nres); /* move yielded values */ 478e3e3a7aSWarner Losh return nres; 488e3e3a7aSWarner Losh } 498e3e3a7aSWarner Losh else { 508e3e3a7aSWarner Losh lua_xmove(co, L, 1); /* move error message */ 518e3e3a7aSWarner Losh return -1; /* error flag */ 528e3e3a7aSWarner Losh } 538e3e3a7aSWarner Losh } 548e3e3a7aSWarner Losh 558e3e3a7aSWarner Losh 568e3e3a7aSWarner Losh static int luaB_coresume (lua_State *L) { 578e3e3a7aSWarner Losh lua_State *co = getco(L); 588e3e3a7aSWarner Losh int r; 598e3e3a7aSWarner Losh r = auxresume(L, co, lua_gettop(L) - 1); 60*8c784bb8SWarner Losh if (l_unlikely(r < 0)) { 618e3e3a7aSWarner Losh lua_pushboolean(L, 0); 628e3e3a7aSWarner Losh lua_insert(L, -2); 638e3e3a7aSWarner Losh return 2; /* return false + error message */ 648e3e3a7aSWarner Losh } 658e3e3a7aSWarner Losh else { 668e3e3a7aSWarner Losh lua_pushboolean(L, 1); 678e3e3a7aSWarner Losh lua_insert(L, -(r + 1)); 688e3e3a7aSWarner Losh return r + 1; /* return true + 'resume' returns */ 698e3e3a7aSWarner Losh } 708e3e3a7aSWarner Losh } 718e3e3a7aSWarner Losh 728e3e3a7aSWarner Losh 738e3e3a7aSWarner Losh static int luaB_auxwrap (lua_State *L) { 748e3e3a7aSWarner Losh lua_State *co = lua_tothread(L, lua_upvalueindex(1)); 758e3e3a7aSWarner Losh int r = auxresume(L, co, lua_gettop(L)); 76*8c784bb8SWarner Losh if (l_unlikely(r < 0)) { /* error? */ 770495ed39SKyle Evans int stat = lua_status(co); 78*8c784bb8SWarner Losh if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ 79*8c784bb8SWarner Losh stat = lua_resetthread(co); /* close its tbc variables */ 80*8c784bb8SWarner Losh lua_assert(stat != LUA_OK); 81*8c784bb8SWarner Losh lua_xmove(co, L, 1); /* move error message to the caller */ 82*8c784bb8SWarner Losh } 830495ed39SKyle Evans if (stat != LUA_ERRMEM && /* not a memory error and ... */ 840495ed39SKyle Evans lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ 850495ed39SKyle Evans luaL_where(L, 1); /* add extra info, if available */ 868e3e3a7aSWarner Losh lua_insert(L, -2); 878e3e3a7aSWarner Losh lua_concat(L, 2); 888e3e3a7aSWarner Losh } 898e3e3a7aSWarner Losh return lua_error(L); /* propagate error */ 908e3e3a7aSWarner Losh } 918e3e3a7aSWarner Losh return r; 928e3e3a7aSWarner Losh } 938e3e3a7aSWarner Losh 948e3e3a7aSWarner Losh 958e3e3a7aSWarner Losh static int luaB_cocreate (lua_State *L) { 968e3e3a7aSWarner Losh lua_State *NL; 978e3e3a7aSWarner Losh luaL_checktype(L, 1, LUA_TFUNCTION); 988e3e3a7aSWarner Losh NL = lua_newthread(L); 998e3e3a7aSWarner Losh lua_pushvalue(L, 1); /* move function to top */ 1008e3e3a7aSWarner Losh lua_xmove(L, NL, 1); /* move function from L to NL */ 1018e3e3a7aSWarner Losh return 1; 1028e3e3a7aSWarner Losh } 1038e3e3a7aSWarner Losh 1048e3e3a7aSWarner Losh 1058e3e3a7aSWarner Losh static int luaB_cowrap (lua_State *L) { 1068e3e3a7aSWarner Losh luaB_cocreate(L); 1078e3e3a7aSWarner Losh lua_pushcclosure(L, luaB_auxwrap, 1); 1088e3e3a7aSWarner Losh return 1; 1098e3e3a7aSWarner Losh } 1108e3e3a7aSWarner Losh 1118e3e3a7aSWarner Losh 1128e3e3a7aSWarner Losh static int luaB_yield (lua_State *L) { 1138e3e3a7aSWarner Losh return lua_yield(L, lua_gettop(L)); 1148e3e3a7aSWarner Losh } 1158e3e3a7aSWarner Losh 1168e3e3a7aSWarner Losh 1170495ed39SKyle Evans #define COS_RUN 0 1180495ed39SKyle Evans #define COS_DEAD 1 1190495ed39SKyle Evans #define COS_YIELD 2 1200495ed39SKyle Evans #define COS_NORM 3 1210495ed39SKyle Evans 1220495ed39SKyle Evans 1230495ed39SKyle Evans static const char *const statname[] = 1240495ed39SKyle Evans {"running", "dead", "suspended", "normal"}; 1250495ed39SKyle Evans 1260495ed39SKyle Evans 1270495ed39SKyle Evans static int auxstatus (lua_State *L, lua_State *co) { 1280495ed39SKyle Evans if (L == co) return COS_RUN; 1298e3e3a7aSWarner Losh else { 1308e3e3a7aSWarner Losh switch (lua_status(co)) { 1318e3e3a7aSWarner Losh case LUA_YIELD: 1320495ed39SKyle Evans return COS_YIELD; 1338e3e3a7aSWarner Losh case LUA_OK: { 1348e3e3a7aSWarner Losh lua_Debug ar; 1350495ed39SKyle Evans if (lua_getstack(co, 0, &ar)) /* does it have frames? */ 1360495ed39SKyle Evans return COS_NORM; /* it is running */ 1378e3e3a7aSWarner Losh else if (lua_gettop(co) == 0) 1380495ed39SKyle Evans return COS_DEAD; 1398e3e3a7aSWarner Losh else 1400495ed39SKyle Evans return COS_YIELD; /* initial state */ 1418e3e3a7aSWarner Losh } 1428e3e3a7aSWarner Losh default: /* some error occurred */ 1430495ed39SKyle Evans return COS_DEAD; 1448e3e3a7aSWarner Losh } 1458e3e3a7aSWarner Losh } 1460495ed39SKyle Evans } 1470495ed39SKyle Evans 1480495ed39SKyle Evans 1490495ed39SKyle Evans static int luaB_costatus (lua_State *L) { 1500495ed39SKyle Evans lua_State *co = getco(L); 1510495ed39SKyle Evans lua_pushstring(L, statname[auxstatus(L, co)]); 1528e3e3a7aSWarner Losh return 1; 1538e3e3a7aSWarner Losh } 1548e3e3a7aSWarner Losh 1558e3e3a7aSWarner Losh 1568e3e3a7aSWarner Losh static int luaB_yieldable (lua_State *L) { 1570495ed39SKyle Evans lua_State *co = lua_isnone(L, 1) ? L : getco(L); 1580495ed39SKyle Evans lua_pushboolean(L, lua_isyieldable(co)); 1598e3e3a7aSWarner Losh return 1; 1608e3e3a7aSWarner Losh } 1618e3e3a7aSWarner Losh 1628e3e3a7aSWarner Losh 1638e3e3a7aSWarner Losh static int luaB_corunning (lua_State *L) { 1648e3e3a7aSWarner Losh int ismain = lua_pushthread(L); 1658e3e3a7aSWarner Losh lua_pushboolean(L, ismain); 1668e3e3a7aSWarner Losh return 2; 1678e3e3a7aSWarner Losh } 1688e3e3a7aSWarner Losh 1698e3e3a7aSWarner Losh 1700495ed39SKyle Evans static int luaB_close (lua_State *L) { 1710495ed39SKyle Evans lua_State *co = getco(L); 1720495ed39SKyle Evans int status = auxstatus(L, co); 1730495ed39SKyle Evans switch (status) { 1740495ed39SKyle Evans case COS_DEAD: case COS_YIELD: { 1750495ed39SKyle Evans status = lua_resetthread(co); 1760495ed39SKyle Evans if (status == LUA_OK) { 1770495ed39SKyle Evans lua_pushboolean(L, 1); 1780495ed39SKyle Evans return 1; 1790495ed39SKyle Evans } 1800495ed39SKyle Evans else { 1810495ed39SKyle Evans lua_pushboolean(L, 0); 182*8c784bb8SWarner Losh lua_xmove(co, L, 1); /* move error message */ 1830495ed39SKyle Evans return 2; 1840495ed39SKyle Evans } 1850495ed39SKyle Evans } 1860495ed39SKyle Evans default: /* normal or running coroutine */ 1870495ed39SKyle Evans return luaL_error(L, "cannot close a %s coroutine", statname[status]); 1880495ed39SKyle Evans } 1890495ed39SKyle Evans } 1900495ed39SKyle Evans 1910495ed39SKyle Evans 1928e3e3a7aSWarner Losh static const luaL_Reg co_funcs[] = { 1938e3e3a7aSWarner Losh {"create", luaB_cocreate}, 1948e3e3a7aSWarner Losh {"resume", luaB_coresume}, 1958e3e3a7aSWarner Losh {"running", luaB_corunning}, 1968e3e3a7aSWarner Losh {"status", luaB_costatus}, 1978e3e3a7aSWarner Losh {"wrap", luaB_cowrap}, 1988e3e3a7aSWarner Losh {"yield", luaB_yield}, 1998e3e3a7aSWarner Losh {"isyieldable", luaB_yieldable}, 2000495ed39SKyle Evans {"close", luaB_close}, 2018e3e3a7aSWarner Losh {NULL, NULL} 2028e3e3a7aSWarner Losh }; 2038e3e3a7aSWarner Losh 2048e3e3a7aSWarner Losh 2058e3e3a7aSWarner Losh 2068e3e3a7aSWarner Losh LUAMOD_API int luaopen_coroutine (lua_State *L) { 2078e3e3a7aSWarner Losh luaL_newlib(L, co_funcs); 2088e3e3a7aSWarner Losh return 1; 2098e3e3a7aSWarner Losh } 2108e3e3a7aSWarner Losh 211