18e3e3a7aSWarner Losh /* 2*0495ed39SKyle 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); 23*0495ed39SKyle Evans luaL_argexpected(L, co, 1, "thread"); 248e3e3a7aSWarner Losh return co; 258e3e3a7aSWarner Losh } 268e3e3a7aSWarner Losh 278e3e3a7aSWarner Losh 28*0495ed39SKyle Evans /* 29*0495ed39SKyle Evans ** Resumes a coroutine. Returns the number of results for non-error 30*0495ed39SKyle Evans ** cases or -1 for errors. 31*0495ed39SKyle Evans */ 328e3e3a7aSWarner Losh static int auxresume (lua_State *L, lua_State *co, int narg) { 33*0495ed39SKyle Evans int status, nres; 348e3e3a7aSWarner Losh if (!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); 39*0495ed39SKyle Evans status = lua_resume(co, L, narg, &nres); 408e3e3a7aSWarner Losh if (status == LUA_OK || status == LUA_YIELD) { 418e3e3a7aSWarner Losh if (!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); 608e3e3a7aSWarner Losh if (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*0495ed39SKyle Evans if (r < 0) { /* error? */ 77*0495ed39SKyle Evans int stat = lua_status(co); 78*0495ed39SKyle Evans if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */ 79*0495ed39SKyle Evans lua_resetthread(co); /* close its tbc variables */ 80*0495ed39SKyle Evans if (stat != LUA_ERRMEM && /* not a memory error and ... */ 81*0495ed39SKyle Evans lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ 82*0495ed39SKyle Evans luaL_where(L, 1); /* add extra info, if available */ 838e3e3a7aSWarner Losh lua_insert(L, -2); 848e3e3a7aSWarner Losh lua_concat(L, 2); 858e3e3a7aSWarner Losh } 868e3e3a7aSWarner Losh return lua_error(L); /* propagate error */ 878e3e3a7aSWarner Losh } 888e3e3a7aSWarner Losh return r; 898e3e3a7aSWarner Losh } 908e3e3a7aSWarner Losh 918e3e3a7aSWarner Losh 928e3e3a7aSWarner Losh static int luaB_cocreate (lua_State *L) { 938e3e3a7aSWarner Losh lua_State *NL; 948e3e3a7aSWarner Losh luaL_checktype(L, 1, LUA_TFUNCTION); 958e3e3a7aSWarner Losh NL = lua_newthread(L); 968e3e3a7aSWarner Losh lua_pushvalue(L, 1); /* move function to top */ 978e3e3a7aSWarner Losh lua_xmove(L, NL, 1); /* move function from L to NL */ 988e3e3a7aSWarner Losh return 1; 998e3e3a7aSWarner Losh } 1008e3e3a7aSWarner Losh 1018e3e3a7aSWarner Losh 1028e3e3a7aSWarner Losh static int luaB_cowrap (lua_State *L) { 1038e3e3a7aSWarner Losh luaB_cocreate(L); 1048e3e3a7aSWarner Losh lua_pushcclosure(L, luaB_auxwrap, 1); 1058e3e3a7aSWarner Losh return 1; 1068e3e3a7aSWarner Losh } 1078e3e3a7aSWarner Losh 1088e3e3a7aSWarner Losh 1098e3e3a7aSWarner Losh static int luaB_yield (lua_State *L) { 1108e3e3a7aSWarner Losh return lua_yield(L, lua_gettop(L)); 1118e3e3a7aSWarner Losh } 1128e3e3a7aSWarner Losh 1138e3e3a7aSWarner Losh 114*0495ed39SKyle Evans #define COS_RUN 0 115*0495ed39SKyle Evans #define COS_DEAD 1 116*0495ed39SKyle Evans #define COS_YIELD 2 117*0495ed39SKyle Evans #define COS_NORM 3 118*0495ed39SKyle Evans 119*0495ed39SKyle Evans 120*0495ed39SKyle Evans static const char *const statname[] = 121*0495ed39SKyle Evans {"running", "dead", "suspended", "normal"}; 122*0495ed39SKyle Evans 123*0495ed39SKyle Evans 124*0495ed39SKyle Evans static int auxstatus (lua_State *L, lua_State *co) { 125*0495ed39SKyle Evans if (L == co) return COS_RUN; 1268e3e3a7aSWarner Losh else { 1278e3e3a7aSWarner Losh switch (lua_status(co)) { 1288e3e3a7aSWarner Losh case LUA_YIELD: 129*0495ed39SKyle Evans return COS_YIELD; 1308e3e3a7aSWarner Losh case LUA_OK: { 1318e3e3a7aSWarner Losh lua_Debug ar; 132*0495ed39SKyle Evans if (lua_getstack(co, 0, &ar)) /* does it have frames? */ 133*0495ed39SKyle Evans return COS_NORM; /* it is running */ 1348e3e3a7aSWarner Losh else if (lua_gettop(co) == 0) 135*0495ed39SKyle Evans return COS_DEAD; 1368e3e3a7aSWarner Losh else 137*0495ed39SKyle Evans return COS_YIELD; /* initial state */ 1388e3e3a7aSWarner Losh } 1398e3e3a7aSWarner Losh default: /* some error occurred */ 140*0495ed39SKyle Evans return COS_DEAD; 1418e3e3a7aSWarner Losh } 1428e3e3a7aSWarner Losh } 143*0495ed39SKyle Evans } 144*0495ed39SKyle Evans 145*0495ed39SKyle Evans 146*0495ed39SKyle Evans static int luaB_costatus (lua_State *L) { 147*0495ed39SKyle Evans lua_State *co = getco(L); 148*0495ed39SKyle Evans lua_pushstring(L, statname[auxstatus(L, co)]); 1498e3e3a7aSWarner Losh return 1; 1508e3e3a7aSWarner Losh } 1518e3e3a7aSWarner Losh 1528e3e3a7aSWarner Losh 1538e3e3a7aSWarner Losh static int luaB_yieldable (lua_State *L) { 154*0495ed39SKyle Evans lua_State *co = lua_isnone(L, 1) ? L : getco(L); 155*0495ed39SKyle Evans lua_pushboolean(L, lua_isyieldable(co)); 1568e3e3a7aSWarner Losh return 1; 1578e3e3a7aSWarner Losh } 1588e3e3a7aSWarner Losh 1598e3e3a7aSWarner Losh 1608e3e3a7aSWarner Losh static int luaB_corunning (lua_State *L) { 1618e3e3a7aSWarner Losh int ismain = lua_pushthread(L); 1628e3e3a7aSWarner Losh lua_pushboolean(L, ismain); 1638e3e3a7aSWarner Losh return 2; 1648e3e3a7aSWarner Losh } 1658e3e3a7aSWarner Losh 1668e3e3a7aSWarner Losh 167*0495ed39SKyle Evans static int luaB_close (lua_State *L) { 168*0495ed39SKyle Evans lua_State *co = getco(L); 169*0495ed39SKyle Evans int status = auxstatus(L, co); 170*0495ed39SKyle Evans switch (status) { 171*0495ed39SKyle Evans case COS_DEAD: case COS_YIELD: { 172*0495ed39SKyle Evans status = lua_resetthread(co); 173*0495ed39SKyle Evans if (status == LUA_OK) { 174*0495ed39SKyle Evans lua_pushboolean(L, 1); 175*0495ed39SKyle Evans return 1; 176*0495ed39SKyle Evans } 177*0495ed39SKyle Evans else { 178*0495ed39SKyle Evans lua_pushboolean(L, 0); 179*0495ed39SKyle Evans lua_xmove(co, L, 1); /* copy error message */ 180*0495ed39SKyle Evans return 2; 181*0495ed39SKyle Evans } 182*0495ed39SKyle Evans } 183*0495ed39SKyle Evans default: /* normal or running coroutine */ 184*0495ed39SKyle Evans return luaL_error(L, "cannot close a %s coroutine", statname[status]); 185*0495ed39SKyle Evans } 186*0495ed39SKyle Evans } 187*0495ed39SKyle Evans 188*0495ed39SKyle Evans 1898e3e3a7aSWarner Losh static const luaL_Reg co_funcs[] = { 1908e3e3a7aSWarner Losh {"create", luaB_cocreate}, 1918e3e3a7aSWarner Losh {"resume", luaB_coresume}, 1928e3e3a7aSWarner Losh {"running", luaB_corunning}, 1938e3e3a7aSWarner Losh {"status", luaB_costatus}, 1948e3e3a7aSWarner Losh {"wrap", luaB_cowrap}, 1958e3e3a7aSWarner Losh {"yield", luaB_yield}, 1968e3e3a7aSWarner Losh {"isyieldable", luaB_yieldable}, 197*0495ed39SKyle Evans {"close", luaB_close}, 1988e3e3a7aSWarner Losh {NULL, NULL} 1998e3e3a7aSWarner Losh }; 2008e3e3a7aSWarner Losh 2018e3e3a7aSWarner Losh 2028e3e3a7aSWarner Losh 2038e3e3a7aSWarner Losh LUAMOD_API int luaopen_coroutine (lua_State *L) { 2048e3e3a7aSWarner Losh luaL_newlib(L, co_funcs); 2058e3e3a7aSWarner Losh return 1; 2068e3e3a7aSWarner Losh } 2078e3e3a7aSWarner Losh 208