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