1*8e3e3a7aSWarner Losh /* 2*8e3e3a7aSWarner Losh ** $Id: lcorolib.c,v 1.10 2016/04/11 19:19:55 roberto Exp $ 3*8e3e3a7aSWarner Losh ** Coroutine Library 4*8e3e3a7aSWarner Losh ** See Copyright Notice in lua.h 5*8e3e3a7aSWarner Losh */ 6*8e3e3a7aSWarner Losh 7*8e3e3a7aSWarner Losh #define lcorolib_c 8*8e3e3a7aSWarner Losh #define LUA_LIB 9*8e3e3a7aSWarner Losh 10*8e3e3a7aSWarner Losh #include "lprefix.h" 11*8e3e3a7aSWarner Losh 12*8e3e3a7aSWarner Losh 13*8e3e3a7aSWarner Losh #include <stdlib.h> 14*8e3e3a7aSWarner Losh 15*8e3e3a7aSWarner Losh #include "lua.h" 16*8e3e3a7aSWarner Losh 17*8e3e3a7aSWarner Losh #include "lauxlib.h" 18*8e3e3a7aSWarner Losh #include "lualib.h" 19*8e3e3a7aSWarner Losh 20*8e3e3a7aSWarner Losh 21*8e3e3a7aSWarner Losh static lua_State *getco (lua_State *L) { 22*8e3e3a7aSWarner Losh lua_State *co = lua_tothread(L, 1); 23*8e3e3a7aSWarner Losh luaL_argcheck(L, co, 1, "thread expected"); 24*8e3e3a7aSWarner Losh return co; 25*8e3e3a7aSWarner Losh } 26*8e3e3a7aSWarner Losh 27*8e3e3a7aSWarner Losh 28*8e3e3a7aSWarner Losh static int auxresume (lua_State *L, lua_State *co, int narg) { 29*8e3e3a7aSWarner Losh int status; 30*8e3e3a7aSWarner Losh if (!lua_checkstack(co, narg)) { 31*8e3e3a7aSWarner Losh lua_pushliteral(L, "too many arguments to resume"); 32*8e3e3a7aSWarner Losh return -1; /* error flag */ 33*8e3e3a7aSWarner Losh } 34*8e3e3a7aSWarner Losh if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) { 35*8e3e3a7aSWarner Losh lua_pushliteral(L, "cannot resume dead coroutine"); 36*8e3e3a7aSWarner Losh return -1; /* error flag */ 37*8e3e3a7aSWarner Losh } 38*8e3e3a7aSWarner Losh lua_xmove(L, co, narg); 39*8e3e3a7aSWarner Losh status = lua_resume(co, L, narg); 40*8e3e3a7aSWarner Losh if (status == LUA_OK || status == LUA_YIELD) { 41*8e3e3a7aSWarner Losh int nres = lua_gettop(co); 42*8e3e3a7aSWarner Losh if (!lua_checkstack(L, nres + 1)) { 43*8e3e3a7aSWarner Losh lua_pop(co, nres); /* remove results anyway */ 44*8e3e3a7aSWarner Losh lua_pushliteral(L, "too many results to resume"); 45*8e3e3a7aSWarner Losh return -1; /* error flag */ 46*8e3e3a7aSWarner Losh } 47*8e3e3a7aSWarner Losh lua_xmove(co, L, nres); /* move yielded values */ 48*8e3e3a7aSWarner Losh return nres; 49*8e3e3a7aSWarner Losh } 50*8e3e3a7aSWarner Losh else { 51*8e3e3a7aSWarner Losh lua_xmove(co, L, 1); /* move error message */ 52*8e3e3a7aSWarner Losh return -1; /* error flag */ 53*8e3e3a7aSWarner Losh } 54*8e3e3a7aSWarner Losh } 55*8e3e3a7aSWarner Losh 56*8e3e3a7aSWarner Losh 57*8e3e3a7aSWarner Losh static int luaB_coresume (lua_State *L) { 58*8e3e3a7aSWarner Losh lua_State *co = getco(L); 59*8e3e3a7aSWarner Losh int r; 60*8e3e3a7aSWarner Losh r = auxresume(L, co, lua_gettop(L) - 1); 61*8e3e3a7aSWarner Losh if (r < 0) { 62*8e3e3a7aSWarner Losh lua_pushboolean(L, 0); 63*8e3e3a7aSWarner Losh lua_insert(L, -2); 64*8e3e3a7aSWarner Losh return 2; /* return false + error message */ 65*8e3e3a7aSWarner Losh } 66*8e3e3a7aSWarner Losh else { 67*8e3e3a7aSWarner Losh lua_pushboolean(L, 1); 68*8e3e3a7aSWarner Losh lua_insert(L, -(r + 1)); 69*8e3e3a7aSWarner Losh return r + 1; /* return true + 'resume' returns */ 70*8e3e3a7aSWarner Losh } 71*8e3e3a7aSWarner Losh } 72*8e3e3a7aSWarner Losh 73*8e3e3a7aSWarner Losh 74*8e3e3a7aSWarner Losh static int luaB_auxwrap (lua_State *L) { 75*8e3e3a7aSWarner Losh lua_State *co = lua_tothread(L, lua_upvalueindex(1)); 76*8e3e3a7aSWarner Losh int r = auxresume(L, co, lua_gettop(L)); 77*8e3e3a7aSWarner Losh if (r < 0) { 78*8e3e3a7aSWarner Losh if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ 79*8e3e3a7aSWarner Losh luaL_where(L, 1); /* add extra info */ 80*8e3e3a7aSWarner Losh lua_insert(L, -2); 81*8e3e3a7aSWarner Losh lua_concat(L, 2); 82*8e3e3a7aSWarner Losh } 83*8e3e3a7aSWarner Losh return lua_error(L); /* propagate error */ 84*8e3e3a7aSWarner Losh } 85*8e3e3a7aSWarner Losh return r; 86*8e3e3a7aSWarner Losh } 87*8e3e3a7aSWarner Losh 88*8e3e3a7aSWarner Losh 89*8e3e3a7aSWarner Losh static int luaB_cocreate (lua_State *L) { 90*8e3e3a7aSWarner Losh lua_State *NL; 91*8e3e3a7aSWarner Losh luaL_checktype(L, 1, LUA_TFUNCTION); 92*8e3e3a7aSWarner Losh NL = lua_newthread(L); 93*8e3e3a7aSWarner Losh lua_pushvalue(L, 1); /* move function to top */ 94*8e3e3a7aSWarner Losh lua_xmove(L, NL, 1); /* move function from L to NL */ 95*8e3e3a7aSWarner Losh return 1; 96*8e3e3a7aSWarner Losh } 97*8e3e3a7aSWarner Losh 98*8e3e3a7aSWarner Losh 99*8e3e3a7aSWarner Losh static int luaB_cowrap (lua_State *L) { 100*8e3e3a7aSWarner Losh luaB_cocreate(L); 101*8e3e3a7aSWarner Losh lua_pushcclosure(L, luaB_auxwrap, 1); 102*8e3e3a7aSWarner Losh return 1; 103*8e3e3a7aSWarner Losh } 104*8e3e3a7aSWarner Losh 105*8e3e3a7aSWarner Losh 106*8e3e3a7aSWarner Losh static int luaB_yield (lua_State *L) { 107*8e3e3a7aSWarner Losh return lua_yield(L, lua_gettop(L)); 108*8e3e3a7aSWarner Losh } 109*8e3e3a7aSWarner Losh 110*8e3e3a7aSWarner Losh 111*8e3e3a7aSWarner Losh static int luaB_costatus (lua_State *L) { 112*8e3e3a7aSWarner Losh lua_State *co = getco(L); 113*8e3e3a7aSWarner Losh if (L == co) lua_pushliteral(L, "running"); 114*8e3e3a7aSWarner Losh else { 115*8e3e3a7aSWarner Losh switch (lua_status(co)) { 116*8e3e3a7aSWarner Losh case LUA_YIELD: 117*8e3e3a7aSWarner Losh lua_pushliteral(L, "suspended"); 118*8e3e3a7aSWarner Losh break; 119*8e3e3a7aSWarner Losh case LUA_OK: { 120*8e3e3a7aSWarner Losh lua_Debug ar; 121*8e3e3a7aSWarner Losh if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ 122*8e3e3a7aSWarner Losh lua_pushliteral(L, "normal"); /* it is running */ 123*8e3e3a7aSWarner Losh else if (lua_gettop(co) == 0) 124*8e3e3a7aSWarner Losh lua_pushliteral(L, "dead"); 125*8e3e3a7aSWarner Losh else 126*8e3e3a7aSWarner Losh lua_pushliteral(L, "suspended"); /* initial state */ 127*8e3e3a7aSWarner Losh break; 128*8e3e3a7aSWarner Losh } 129*8e3e3a7aSWarner Losh default: /* some error occurred */ 130*8e3e3a7aSWarner Losh lua_pushliteral(L, "dead"); 131*8e3e3a7aSWarner Losh break; 132*8e3e3a7aSWarner Losh } 133*8e3e3a7aSWarner Losh } 134*8e3e3a7aSWarner Losh return 1; 135*8e3e3a7aSWarner Losh } 136*8e3e3a7aSWarner Losh 137*8e3e3a7aSWarner Losh 138*8e3e3a7aSWarner Losh static int luaB_yieldable (lua_State *L) { 139*8e3e3a7aSWarner Losh lua_pushboolean(L, lua_isyieldable(L)); 140*8e3e3a7aSWarner Losh return 1; 141*8e3e3a7aSWarner Losh } 142*8e3e3a7aSWarner Losh 143*8e3e3a7aSWarner Losh 144*8e3e3a7aSWarner Losh static int luaB_corunning (lua_State *L) { 145*8e3e3a7aSWarner Losh int ismain = lua_pushthread(L); 146*8e3e3a7aSWarner Losh lua_pushboolean(L, ismain); 147*8e3e3a7aSWarner Losh return 2; 148*8e3e3a7aSWarner Losh } 149*8e3e3a7aSWarner Losh 150*8e3e3a7aSWarner Losh 151*8e3e3a7aSWarner Losh static const luaL_Reg co_funcs[] = { 152*8e3e3a7aSWarner Losh {"create", luaB_cocreate}, 153*8e3e3a7aSWarner Losh {"resume", luaB_coresume}, 154*8e3e3a7aSWarner Losh {"running", luaB_corunning}, 155*8e3e3a7aSWarner Losh {"status", luaB_costatus}, 156*8e3e3a7aSWarner Losh {"wrap", luaB_cowrap}, 157*8e3e3a7aSWarner Losh {"yield", luaB_yield}, 158*8e3e3a7aSWarner Losh {"isyieldable", luaB_yieldable}, 159*8e3e3a7aSWarner Losh {NULL, NULL} 160*8e3e3a7aSWarner Losh }; 161*8e3e3a7aSWarner Losh 162*8e3e3a7aSWarner Losh 163*8e3e3a7aSWarner Losh 164*8e3e3a7aSWarner Losh LUAMOD_API int luaopen_coroutine (lua_State *L) { 165*8e3e3a7aSWarner Losh luaL_newlib(L, co_funcs); 166*8e3e3a7aSWarner Losh return 1; 167*8e3e3a7aSWarner Losh } 168*8e3e3a7aSWarner Losh 169