xref: /freebsd/contrib/lua/src/lcorolib.c (revision a9490b81)
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 
getco(lua_State * L)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 */
auxresume(lua_State * L,lua_State * co,int narg)328e3e3a7aSWarner Losh static int auxresume (lua_State *L, lua_State *co, int narg) {
330495ed39SKyle Evans   int status, nres;
348c784bb8SWarner 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);
408c784bb8SWarner Losh   if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
418c784bb8SWarner 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 
luaB_coresume(lua_State * L)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);
608c784bb8SWarner 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 
luaB_auxwrap(lua_State * L)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));
768c784bb8SWarner Losh   if (l_unlikely(r < 0)) {  /* error? */
770495ed39SKyle Evans     int stat = lua_status(co);
788c784bb8SWarner Losh     if (stat != LUA_OK && stat != LUA_YIELD) {  /* error in the coroutine? */
79*a9490b81SWarner Losh       stat = lua_closethread(co, L);  /* close its tbc variables */
808c784bb8SWarner Losh       lua_assert(stat != LUA_OK);
818c784bb8SWarner Losh       lua_xmove(co, L, 1);  /* move error message to the caller */
828c784bb8SWarner 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 
luaB_cocreate(lua_State * L)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 
luaB_cowrap(lua_State * L)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 
luaB_yield(lua_State * L)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 
auxstatus(lua_State * L,lua_State * co)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 
luaB_costatus(lua_State * L)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 
luaB_yieldable(lua_State * L)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 
luaB_corunning(lua_State * L)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 
luaB_close(lua_State * L)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: {
175*a9490b81SWarner Losh       status = lua_closethread(co, L);
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);
1828c784bb8SWarner 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 
luaopen_coroutine(lua_State * L)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