1 /* $NetBSD: lcorolib.c,v 1.10 2023/06/08 21:12:08 nikita Exp $ */ 2 3 /* 4 ** Id: lcorolib.c 5 ** Coroutine Library 6 ** See Copyright Notice in lua.h 7 */ 8 9 #define lcorolib_c 10 #define LUA_LIB 11 12 #include "lprefix.h" 13 14 15 #ifndef _KERNEL 16 #include <stdlib.h> 17 #endif /* _KERNEL */ 18 19 #include "lua.h" 20 21 #include "lauxlib.h" 22 #include "lualib.h" 23 24 25 static lua_State *getco (lua_State *L) { 26 lua_State *co = lua_tothread(L, 1); 27 luaL_argexpected(L, co, 1, "thread"); 28 return co; 29 } 30 31 32 /* 33 ** Resumes a coroutine. Returns the number of results for non-error 34 ** cases or -1 for errors. 35 */ 36 static int auxresume (lua_State *L, lua_State *co, int narg) { 37 int status, nres; 38 if (l_unlikely(!lua_checkstack(co, narg))) { 39 lua_pushliteral(L, "too many arguments to resume"); 40 return -1; /* error flag */ 41 } 42 lua_xmove(L, co, narg); 43 status = lua_resume(co, L, narg, &nres); 44 if (l_likely(status == LUA_OK || status == LUA_YIELD)) { 45 if (l_unlikely(!lua_checkstack(L, nres + 1))) { 46 lua_pop(co, nres); /* remove results anyway */ 47 lua_pushliteral(L, "too many results to resume"); 48 return -1; /* error flag */ 49 } 50 lua_xmove(co, L, nres); /* move yielded values */ 51 return nres; 52 } 53 else { 54 lua_xmove(co, L, 1); /* move error message */ 55 return -1; /* error flag */ 56 } 57 } 58 59 60 static int luaB_coresume (lua_State *L) { 61 lua_State *co = getco(L); 62 int r; 63 r = auxresume(L, co, lua_gettop(L) - 1); 64 if (l_unlikely(r < 0)) { 65 lua_pushboolean(L, 0); 66 lua_insert(L, -2); 67 return 2; /* return false + error message */ 68 } 69 else { 70 lua_pushboolean(L, 1); 71 lua_insert(L, -(r + 1)); 72 return r + 1; /* return true + 'resume' returns */ 73 } 74 } 75 76 77 static int luaB_auxwrap (lua_State *L) { 78 lua_State *co = lua_tothread(L, lua_upvalueindex(1)); 79 int r = auxresume(L, co, lua_gettop(L)); 80 if (l_unlikely(r < 0)) { /* error? */ 81 int stat = lua_status(co); 82 if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ 83 stat = lua_closethread(co, L); /* close its tbc variables */ 84 lua_assert(stat != LUA_OK); 85 lua_xmove(co, L, 1); /* move error message to the caller */ 86 } 87 if (stat != LUA_ERRMEM && /* not a memory error and ... */ 88 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ 89 luaL_where(L, 1); /* add extra info, if available */ 90 lua_insert(L, -2); 91 lua_concat(L, 2); 92 } 93 return lua_error(L); /* propagate error */ 94 } 95 return r; 96 } 97 98 99 static int luaB_cocreate (lua_State *L) { 100 lua_State *NL; 101 luaL_checktype(L, 1, LUA_TFUNCTION); 102 NL = lua_newthread(L); 103 lua_pushvalue(L, 1); /* move function to top */ 104 lua_xmove(L, NL, 1); /* move function from L to NL */ 105 return 1; 106 } 107 108 109 static int luaB_cowrap (lua_State *L) { 110 luaB_cocreate(L); 111 lua_pushcclosure(L, luaB_auxwrap, 1); 112 return 1; 113 } 114 115 116 static int luaB_yield (lua_State *L) { 117 return lua_yield(L, lua_gettop(L)); 118 } 119 120 121 #define COS_RUN 0 122 #define COS_DEAD 1 123 #define COS_YIELD 2 124 #define COS_NORM 3 125 126 127 static const char *const statname[] = 128 {"running", "dead", "suspended", "normal"}; 129 130 131 static int auxstatus (lua_State *L, lua_State *co) { 132 if (L == co) return COS_RUN; 133 else { 134 switch (lua_status(co)) { 135 case LUA_YIELD: 136 return COS_YIELD; 137 case LUA_OK: { 138 lua_Debug ar; 139 if (lua_getstack(co, 0, &ar)) /* does it have frames? */ 140 return COS_NORM; /* it is running */ 141 else if (lua_gettop(co) == 0) 142 return COS_DEAD; 143 else 144 return COS_YIELD; /* initial state */ 145 } 146 default: /* some error occurred */ 147 return COS_DEAD; 148 } 149 } 150 } 151 152 153 static int luaB_costatus (lua_State *L) { 154 lua_State *co = getco(L); 155 lua_pushstring(L, statname[auxstatus(L, co)]); 156 return 1; 157 } 158 159 160 static int luaB_yieldable (lua_State *L) { 161 lua_State *co = lua_isnone(L, 1) ? L : getco(L); 162 lua_pushboolean(L, lua_isyieldable(co)); 163 return 1; 164 } 165 166 167 static int luaB_corunning (lua_State *L) { 168 int ismain = lua_pushthread(L); 169 lua_pushboolean(L, ismain); 170 return 2; 171 } 172 173 174 static int luaB_close (lua_State *L) { 175 lua_State *co = getco(L); 176 int status = auxstatus(L, co); 177 switch (status) { 178 case COS_DEAD: case COS_YIELD: { 179 status = lua_closethread(co, L); 180 if (status == LUA_OK) { 181 lua_pushboolean(L, 1); 182 return 1; 183 } 184 else { 185 lua_pushboolean(L, 0); 186 lua_xmove(co, L, 1); /* move error message */ 187 return 2; 188 } 189 } 190 default: /* normal or running coroutine */ 191 return luaL_error(L, "cannot close a %s coroutine", statname[status]); 192 } 193 } 194 195 196 static const luaL_Reg co_funcs[] = { 197 {"create", luaB_cocreate}, 198 {"resume", luaB_coresume}, 199 {"running", luaB_corunning}, 200 {"status", luaB_costatus}, 201 {"wrap", luaB_cowrap}, 202 {"yield", luaB_yield}, 203 {"isyieldable", luaB_yieldable}, 204 {"close", luaB_close}, 205 {NULL, NULL} 206 }; 207 208 209 210 LUAMOD_API int luaopen_coroutine (lua_State *L) { 211 luaL_newlib(L, co_funcs); 212 return 1; 213 } 214 215