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