new API for coroutines

This commit is contained in:
Roberto Ierusalimschy 2002-11-06 17:08:00 -02:00
parent 6820da5096
commit 118347d8c3
6 changed files with 85 additions and 88 deletions

15
lapi.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lapi.c,v 1.214 2002/10/25 20:05:28 roberto Exp roberto $ ** $Id: lapi.c,v 1.215 2002/10/25 21:31:28 roberto Exp roberto $
** Lua API ** Lua API
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -105,6 +105,19 @@ LUA_API int lua_checkstack (lua_State *L, int size) {
} }
LUA_API void lua_movethread (lua_State *from, lua_State *to, int n) {
int i;
lua_lock(to);
api_checknelems(from, n);
from->top -= n;
for (i = 0; i < n; i++) {
setobj(to->top, from->top + i);
api_incr_top(to);
}
lua_unlock(to);
}
LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
lua_CFunction old; lua_CFunction old;
lua_lock(L); lua_lock(L);

View File

@ -1,5 +1,5 @@
/* /*
** $Id: lbaselib.c,v 1.102 2002/10/25 21:31:28 roberto Exp roberto $ ** $Id: lbaselib.c,v 1.103 2002/10/25 21:36:54 roberto Exp roberto $
** Basic library ** Basic library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -557,12 +557,23 @@ static const luaL_reg base_funcs[] = {
** ======================================================= ** =======================================================
*/ */
static int auxresume (lua_State *L, lua_State *co, int narg) {
static int luaB_auxresume (lua_State *L, lua_State *co) {
int status; int status;
int oldtop = lua_gettop(L); if (!lua_checkstack(co, narg))
status = lua_resume(L, co); luaL_error(L, "too many arguments to resume");
return (status != 0) ? -1 : lua_gettop(L) - oldtop; lua_movethread(L, co, narg);
status = lua_resume(co, narg);
if (status == 0) {
int nres = lua_gettop(co);
if (!lua_checkstack(L, narg))
luaL_error(L, "too many results to resume");
lua_movethread(co, L, nres); /* move yielded values */
return nres;
}
else {
lua_movethread(co, L, 1); /* move error message */
return -1; /* error flag */
}
} }
@ -570,7 +581,7 @@ static int luaB_coresume (lua_State *L) {
lua_State *co = lua_tothread(L, 1); lua_State *co = lua_tothread(L, 1);
int r; int r;
luaL_arg_check(L, co, 1, "coroutine/thread expected"); luaL_arg_check(L, co, 1, "coroutine/thread expected");
r = luaB_auxresume(L, co); r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) { if (r < 0) {
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
lua_insert(L, -2); lua_insert(L, -2);
@ -585,28 +596,19 @@ static int luaB_coresume (lua_State *L) {
static int luaB_auxwrap (lua_State *L) { static int luaB_auxwrap (lua_State *L) {
int r = luaB_auxresume(L, lua_tothread(L, lua_upvalueindex(1))); lua_State *co = lua_tothread(L, lua_upvalueindex(1));
if (r < 0) lua_error(L); int r = auxresume(L, co, lua_gettop(L));
if (r < 0) lua_error(L); /* propagate error */
return r; return r;
} }
static int luaB_cocreate (lua_State *L) { static int luaB_cocreate (lua_State *L) {
lua_State *NL; lua_State *NL = lua_newthread(L);
int ref;
int i;
int n = lua_gettop(L);
luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
"Lua function expected"); "Lua function expected");
NL = lua_newthread(L); lua_pushvalue(L, 1); /* move function to top */
/* move function and arguments from L to NL */ lua_movethread(L, NL, 1); /* move function from L to NL */
for (i = 1; i <= n; i++) {
lua_pushvalue(L, i);
ref = lua_ref(L, 1);
lua_getref(NL, ref);
lua_unref(L, ref);
}
lua_cobegin(NL, n-1);
return 1; return 1;
} }

97
ldo.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: ldo.c,v 1.196 2002/10/09 13:42:01 roberto Exp roberto $ ** $Id: ldo.c,v 1.197 2002/10/25 20:05:28 roberto Exp roberto $
** Stack and Call structure of Lua ** Stack and Call structure of Lua
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -44,22 +44,23 @@ struct lua_longjmp {
}; };
static void seterrorobj (lua_State *L, int errcode) { static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) { switch (errcode) {
case LUA_ERRMEM: { case LUA_ERRMEM: {
setsvalue(L->top, luaS_new(L, MEMERRMSG)); setsvalue(oldtop, luaS_new(L, MEMERRMSG));
break; break;
} }
case LUA_ERRERR: { case LUA_ERRERR: {
setsvalue(L->top, luaS_new(L, "error in error handling")); setsvalue(oldtop, luaS_new(L, "error in error handling"));
break; break;
} }
case LUA_ERRSYNTAX: case LUA_ERRSYNTAX:
case LUA_ERRRUN: { case LUA_ERRRUN: {
return; /* error message already on top */ setobj(oldtop, L->top - 1); /* error message on current top */
break;
} }
} }
L->top++; L->top = oldtop + 1;
} }
@ -298,67 +299,46 @@ void luaD_call (lua_State *L, StkId func, int nResults) {
} }
LUA_API void lua_cobegin (lua_State *L, int nargs) {
lua_lock(L);
luaD_precall(L, L->top - (nargs+1));
lua_unlock(L);
}
static void move_results (lua_State *L, TObject *from, TObject *to) {
while (from < to) {
setobj(L->top, from);
from++;
incr_top(L);
}
}
static void resume (lua_State *L, void *ud) { static void resume (lua_State *L, void *ud) {
StkId firstResult; StkId firstResult;
int nargs = *cast(int *, ud);
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (ci->state & CI_C) { /* not first time (i.e. inside a yield)? */ if (ci == L->base_ci) { /* no activation record? */
if (nargs >= L->top - L->ci->base)
luaG_runerror(L, "cannot resume dead coroutine");
luaD_precall(L, L->top - (nargs + 1)); /* start coroutine */
}
else if (ci->state && CI_YIELD) { /* inside a yield? */
/* finish interrupted execution of `OP_CALL' */ /* finish interrupted execution of `OP_CALL' */
int nresults; int nresults;
lua_assert((ci-1)->state & CI_SAVEDPC); lua_assert((ci-1)->state & CI_SAVEDPC);
lua_assert(GET_OPCODE(*((ci-1)->u.l.savedpc - 1)) == OP_CALL || lua_assert(GET_OPCODE(*((ci-1)->u.l.savedpc - 1)) == OP_CALL ||
GET_OPCODE(*((ci-1)->u.l.savedpc - 1)) == OP_TAILCALL); GET_OPCODE(*((ci-1)->u.l.savedpc - 1)) == OP_TAILCALL);
nresults = GETARG_C(*((ci-1)->u.l.savedpc - 1)) - 1; nresults = GETARG_C(*((ci-1)->u.l.savedpc - 1)) - 1;
luaD_poscall(L, nresults, L->top); /* complete it */ luaD_poscall(L, nresults, L->top - nargs); /* complete it */
if (nresults >= 0) L->top = L->ci->top; if (nresults >= 0) L->top = L->ci->top;
} }
else
luaG_runerror(L, "cannot resume non-suspended coroutine");
firstResult = luaV_execute(L); firstResult = luaV_execute(L);
if (firstResult == NULL) /* yield? */ if (firstResult != NULL) /* return? */
*cast(int *, ud) = L->ci->u.c.yield_results;
else { /* return */
*cast(int *, ud) = L->top - firstResult;
luaD_poscall(L, LUA_MULTRET, firstResult); /* finalize this coroutine */ luaD_poscall(L, LUA_MULTRET, firstResult); /* finalize this coroutine */
}
} }
LUA_API int lua_resume (lua_State *L, lua_State *co) { LUA_API int lua_resume (lua_State *L, int nargs) {
CallInfo *ci;
int numres;
int status; int status;
int old_allowhooks;
lua_lock(L); lua_lock(L);
ci = co->ci; old_allowhooks = allowhook(L);
if (ci == co->base_ci) { /* no activation record? ?? */ lua_assert(L->errfunc == 0);
luaO_pushfstring(L, "cannot resume dead thread"); status = luaD_rawrunprotected(L, resume, &nargs);
status = LUA_ERRRUN; if (status != 0) {
} L->ci = L->base_ci; /* `kill' thread (??) */
else if (co->errorJmp != NULL) { /* ?? */ seterrorobj(L, status, L->ci->base);
luaO_pushfstring(L, "cannot resume active thread"); luaF_close(L, L->top); /* close eventual pending closures */
status = LUA_ERRRUN; setallowhook(L, old_allowhooks);
} restore_stack_limit(L);
else {
status = luaD_rawrunprotected(co, resume, &numres);
if (status == 0)
move_results(L, co->top - numres, co->top);
else {
setobj(L->top++, co->top - 1); /* move error message to other stack */
co->ci = co->base_ci; /* `kill' thread */
}
} }
lua_unlock(L); lua_unlock(L);
return status; return status;
@ -372,7 +352,12 @@ LUA_API int lua_yield (lua_State *L, int nresults) {
if ((ci-1)->state & CI_C) if ((ci-1)->state & CI_C)
luaG_runerror(L, "cannot yield a C function"); luaG_runerror(L, "cannot yield a C function");
lua_assert(ci->state & CI_C); /* current function is not Lua */ lua_assert(ci->state & CI_C); /* current function is not Lua */
ci->u.c.yield_results = nresults; if (L->top - nresults > ci->base) { /* is there garbage in the stack? */
int i;
for (i=0; i<nresults; i++) /* move down results */
setobj(ci->base + i, L->top - nresults + i);
L->top = ci->base + nresults;
}
lua_unlock(L); lua_unlock(L);
return -1; return -1;
} }
@ -405,12 +390,8 @@ int luaD_pcall (lua_State *L, int nargs, int nresults, ptrdiff_t errfunc) {
c.nresults = nresults; c.nresults = nresults;
status = luaD_rawrunprotected(L, &f_call, &c); status = luaD_rawrunprotected(L, &f_call, &c);
if (status != 0) { /* an error occurred? */ if (status != 0) { /* an error occurred? */
StkId err; /* error msg. position */ StkId oldtop = restorestack(L, old_top) - (nargs+1);
seterrorobj(L, status); seterrorobj(L, status, oldtop);
err = L->top - 1;
/* remove parameters and func from the stack */
L->top = restorestack(L, old_top) - (nargs+1);
setobj(L->top++, err); /* copy error message to corrected top */
luaF_close(L, L->top); /* close eventual pending closures */ luaF_close(L, L->top); /* close eventual pending closures */
L->ci = restoreci(L, old_ci); L->ci = restoreci(L, old_ci);
setallowhook(L, old_allowhooks); setallowhook(L, old_allowhooks);
@ -461,9 +442,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, int bin) {
} }
else { /* error */ else { /* error */
StkId oldtop = restorestack(L, oldtopr); StkId oldtop = restorestack(L, oldtopr);
seterrorobj(L, status); seterrorobj(L, status, oldtop);
setobj(oldtop, L->top - 1); /* copy error message to old top */
L->top = oldtop+1;
} }
return status; return status;
} }

View File

@ -1,5 +1,5 @@
/* /*
** $Id: lstate.h,v 1.98 2002/10/22 17:58:14 roberto Exp roberto $ ** $Id: lstate.h,v 1.99 2002/10/25 20:05:28 roberto Exp roberto $
** Global State ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -83,7 +83,7 @@ typedef struct CallInfo {
StkId *pb; /* points to `base' variable in `luaV_execute' */ StkId *pb; /* points to `base' variable in `luaV_execute' */
} l; } l;
struct { /* for C functions */ struct { /* for C functions */
int yield_results; int dummy; /* just to avoid an empty struct */
} c; } c;
} u; } u;
} CallInfo; } CallInfo;
@ -99,6 +99,7 @@ typedef struct CallInfo {
`pc' is being used by the other, and therefore CI_SAVEDPC is 1 too) */ `pc' is being used by the other, and therefore CI_SAVEDPC is 1 too) */
#define CI_CALLING 4 #define CI_CALLING 4
#define CI_SAVEDPC 8 /* 1 if `savedpc' is updated */ #define CI_SAVEDPC 8 /* 1 if `savedpc' is updated */
#define CI_YIELD 16 /* 1 if thread is suspended */
#define ci_func(ci) (clvalue((ci)->base - 1)) #define ci_func(ci) (clvalue((ci)->base - 1))

7
lua.h
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lua.h,v 1.160 2002/10/25 20:05:28 roberto Exp roberto $ ** $Id: lua.h,v 1.161 2002/10/25 21:31:28 roberto Exp roberto $
** Lua - An Extensible Extension Language ** Lua - An Extensible Extension Language
** Tecgraf: Computer Graphics Technology Group, PUC-Rio, Brazil ** Tecgraf: Computer Graphics Technology Group, PUC-Rio, Brazil
** http://www.lua.org mailto:info@lua.org ** http://www.lua.org mailto:info@lua.org
@ -123,6 +123,8 @@ LUA_API void lua_insert (lua_State *L, int idx);
LUA_API void lua_replace (lua_State *L, int idx); LUA_API void lua_replace (lua_State *L, int idx);
LUA_API int lua_checkstack (lua_State *L, int sz); LUA_API int lua_checkstack (lua_State *L, int sz);
LUA_API void lua_movethread (lua_State *from, lua_State *to, int n);
/* /*
** access functions (stack -> C) ** access functions (stack -> C)
@ -201,9 +203,8 @@ LUA_API int lua_dump (lua_State *L, lua_Chunkwriter writer, void *data);
/* /*
** coroutine functions ** coroutine functions
*/ */
LUA_API void lua_cobegin (lua_State *L, int nargs);
LUA_API int lua_yield (lua_State *L, int nresults); LUA_API int lua_yield (lua_State *L, int nresults);
LUA_API int lua_resume (lua_State *L, lua_State *co); LUA_API int lua_resume (lua_State *L, int narg);
/* /*
** Garbage-collection functions ** Garbage-collection functions

3
lvm.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lvm.c,v 1.257 2002/10/08 18:46:08 roberto Exp roberto $ ** $Id: lvm.c,v 1.258 2002/10/25 20:05:28 roberto Exp roberto $
** Lua virtual machine ** Lua virtual machine
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -592,6 +592,7 @@ StkId luaV_execute (lua_State *L) {
if (firstResult > L->top) { /* yield? */ if (firstResult > L->top) { /* yield? */
(L->ci - 1)->u.l.savedpc = pc; (L->ci - 1)->u.l.savedpc = pc;
(L->ci - 1)->state = CI_SAVEDPC; (L->ci - 1)->state = CI_SAVEDPC;
L->ci->state |= CI_YIELD;
return NULL; return NULL;
} }
/* it was a C function (`precall' called it); adjust results */ /* it was a C function (`precall' called it); adjust results */