zombiezen.com/go/lua@v0.0.0-20231013005828-290725fb9140/internal/lua54/lcorolib.c (about)

     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  
    21  static 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  */
    32  static 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  
    56  static 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  
    73  static 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  
    95  static 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  
   105  static int luaB_cowrap (lua_State *L) {
   106    luaB_cocreate(L);
   107    lua_pushcclosure(L, luaB_auxwrap, 1);
   108    return 1;
   109  }
   110  
   111  
   112  static 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  
   123  static const char *const statname[] =
   124    {"running", "dead", "suspended", "normal"};
   125  
   126  
   127  static 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  
   149  static 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  
   156  static 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  
   163  static int luaB_corunning (lua_State *L) {
   164    int ismain = lua_pushthread(L);
   165    lua_pushboolean(L, ismain);
   166    return 2;
   167  }
   168  
   169  
   170  static 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  
   192  static 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  
   206  LUAMOD_API int luaopen_coroutine (lua_State *L) {
   207    luaL_newlib(L, co_funcs);
   208    return 1;
   209  }
   210