xref: /freebsd/contrib/lua/src/lcorolib.c (revision 0495ed398c4f64013bab2327eb13a303e1f90c13)
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