first implementation of coroutines

This commit is contained in:
Roberto Ierusalimschy 2002-01-09 20:02:47 -02:00
parent 3533382a1e
commit f083812c02
11 changed files with 229 additions and 137 deletions

View File

@ -148,30 +148,6 @@ static int luaB_eventtable (lua_State *L) {
}
static int luaB_weakmode (lua_State *L) {
const char *mode = luaL_check_string(L, 2);
luaL_check_type(L, 1, LUA_TTABLE);
if (*mode == '?') {
char buff[3];
char *s = buff;
int imode = lua_getweakmode(L, 1);
if (imode & LUA_WEAK_KEY) *s++ = 'k';
if (imode & LUA_WEAK_VALUE) *s++ = 'v';
*s = '\0';
lua_pushstring(L, buff);
return 1;
}
else {
int imode = 0;
if (strchr(mode, 'k')) imode |= LUA_WEAK_KEY;
if (strchr(mode, 'v')) imode |= LUA_WEAK_VALUE;
lua_pushvalue(L, 1); /* push table */
lua_setweakmode(L, imode);
return 1; /* return the table */
}
}
static int luaB_globals (lua_State *L) {
lua_getglobals(L); /* value to be returned */
if (!lua_isnone(L, 1)) {
@ -287,6 +263,14 @@ static int luaB_loadfile (lua_State *L) {
}
static int luaB_assert (lua_State *L) {
luaL_check_any(L, 1);
if (!lua_istrue(L, 1))
luaL_verror(L, "assertion failed! %.90s", luaL_opt_string(L, 2, ""));
lua_settop(L, 1);
return 1;
}
#define LUA_PATH "LUA_PATH"
@ -428,6 +412,40 @@ static int luaB_tostring (lua_State *L) {
}
static int luaB_resume (lua_State *L) {
lua_State *co = (lua_State *)lua_touserdata(L, lua_upvalueindex(1));
lua_resume(L, co);
return 0;
}
static int luaB_coroutine (lua_State *L) {
lua_State *NL;
int ref;
luaL_check_type(L, 1, LUA_TFUNCTION);
NL = lua_newthread(L, 0);
if (NL == NULL) lua_error(L, "unable to create new thread");
/* move function from L to NL */
ref = lua_ref(L, 1);
lua_getref(NL, ref);
lua_unref(L, ref);
lua_cobegin(NL, 0);
lua_newuserdatabox(L, NL);
lua_pushcclosure(L, luaB_resume, 1);
return 1;
}
static int luaB_yield (lua_State *L) {
return lua_yield(L, 0);
}
/*
** {======================================================
** Auxiliar table-related functions
*/
static int luaB_foreachi (lua_State *L) {
int n, i;
luaL_check_type(L, 1, LUA_TTABLE);
@ -464,15 +482,6 @@ static int luaB_foreach (lua_State *L) {
}
static int luaB_assert (lua_State *L) {
luaL_check_any(L, 1);
if (!lua_istrue(L, 1))
luaL_verror(L, "assertion failed! %.90s", luaL_opt_string(L, 2, ""));
lua_settop(L, 1);
return 1;
}
static int luaB_getn (lua_State *L) {
luaL_check_type(L, 1, LUA_TTABLE);
lua_pushnumber(L, lua_getn(L, 1));
@ -521,7 +530,6 @@ static int luaB_tremove (lua_State *L) {
/*
** {======================================================
** Quicksort
@ -627,6 +635,8 @@ static int luaB_sort (lua_State *L) {
/* }====================================================== */
/* }====================================================== */
static const luaL_reg base_funcs[] = {
@ -634,6 +644,7 @@ static const luaL_reg base_funcs[] = {
{LUA_ERRORMESSAGE, luaB__ERRORMESSAGE},
{"call", luaB_call},
{"collectgarbage", luaB_collectgarbage},
{"coroutine", luaB_coroutine},
{"dofile", luaB_dofile},
{"dostring", luaB_dostring},
{"error", luaB_error},
@ -659,7 +670,7 @@ static const luaL_reg base_funcs[] = {
{"tinsert", luaB_tinsert},
{"tremove", luaB_tremove},
{"unpack", luaB_unpack},
{"weakmode", luaB_weakmode}
{"yield", luaB_yield}
};

View File

@ -264,8 +264,7 @@ static int nil_constant (FuncState *fs) {
void luaK_setcallreturns (FuncState *fs, expdesc *e, int nresults) {
if (e->k == VCALL) { /* expression is an open function call? */
if (nresults == LUA_MULTRET) nresults = NO_REG;
SETARG_C(getcode(fs, e), nresults);
SETARG_C(getcode(fs, e), nresults+1);
if (nresults == 1) { /* `regular' expression? */
e->k = VNONRELOC;
e->u.i.info = GETARG_A(getcode(fs, e));

View File

@ -1,5 +1,5 @@
/*
** $Id: ldebug.c,v 1.1 2001/11/29 22:14:34 rieru Exp rieru $
** $Id: ldebug.c,v 1.96 2001/12/18 20:52:30 roberto Exp $
** Debug Interface
** See Copyright Notice in lua.h
*/
@ -303,7 +303,7 @@ static int checkopenop (const Proto *pt, int pc) {
switch (GET_OPCODE(i)) {
case OP_CALL:
case OP_RETURN: {
check(GETARG_B(i) == NO_REG);
check(GETARG_B(i) == 0);
return 1;
}
case OP_SETLISTO: return 1;
@ -391,10 +391,11 @@ static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) {
break;
}
case OP_CALL: {
if (b != NO_REG) {
checkreg(pt, a+b);
if (b != 0) {
checkreg(pt, a+b-1);
}
if (c == NO_REG) {
c--; /* c = num. returns */
if (c == LUA_MULTRET) {
check(checkopenop(pt, pc));
}
else if (c != 0)
@ -403,8 +404,8 @@ static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) {
break;
}
case OP_RETURN: {
if (b != NO_REG && b != 0)
checkreg(pt, a+b-1);
b--; /* b = num. returns */
if (b > 0) checkreg(pt, a+b-1);
break;
}
case OP_FORPREP:

126
ldo.c
View File

@ -18,6 +18,7 @@
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
@ -112,8 +113,6 @@ void luaD_callHook (lua_State *L, lua_Hook callhook, const char *event) {
}
#define newci(L) ((++L->ci == L->end_ci) ? growci(L) : L->ci)
static CallInfo *growci (lua_State *L) {
lua_assert(L->ci == L->end_ci);
luaM_reallocvector(L, L->base_ci, L->size_ci, 2*L->size_ci, CallInfo);
@ -124,9 +123,32 @@ static CallInfo *growci (lua_State *L) {
}
static void adjust_varargs (lua_State *L, StkId base, int nfixargs) {
int i;
Table *htab;
TObject n, nname;
StkId firstvar = base + nfixargs; /* position of first vararg */
if (L->top < firstvar) {
luaD_checkstack(L, firstvar - L->top);
while (L->top < firstvar)
setnilvalue(L->top++);
}
htab = luaH_new(L, 0, 0);
for (i=0; firstvar+i<L->top; i++)
luaH_setnum(L, htab, i+1, firstvar+i);
/* store counter in field `n' */
setnvalue(&n, i);
setsvalue(&nname, luaS_newliteral(L, "n"));
luaH_set(L, htab, &nname, &n);
L->top = firstvar; /* remove elements from the stack */
sethvalue(L->top, htab);
incr_top;
}
StkId luaD_precall (lua_State *L, StkId func) {
CallInfo *ci;
int n;
LClosure *cl;
if (ttype(func) != LUA_TFUNCTION) {
/* `func' is not a function; check the `function' tag method */
const TObject *tm = luaT_gettmbyobj(L, func, TM_CALL);
@ -135,21 +157,39 @@ StkId luaD_precall (lua_State *L, StkId func) {
luaD_openstack(L, func);
setobj(func, tm); /* tag method is the new function to be called */
}
ci = newci(L);
ci = ++L->ci;
if (L->ci == L->end_ci) ci = growci(L);
ci->base = func+1;
ci->savedpc = NULL;
if (L->callhook)
luaD_callHook(L, L->callhook, "call");
if (!clvalue(func)->c.isC) return NULL;
/* if is a C function, call it */
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
lua_unlock(L);
cl = &clvalue(func)->l;
if (!cl->isC) { /* Lua function? prepare its call */
StkId base = func+1;
Proto *p = cl->p;
ci->linehook = L->linehook;
ci->savedpc = p->code; /* starting point */
if (p->is_vararg) /* varargs? */
adjust_varargs(L, base, p->numparams);
if (base > L->stack_last - p->maxstacksize)
luaD_stackerror(L);
ci->top = base + p->maxstacksize;
while (L->top < ci->top)
setnilvalue(L->top++);
L->top = ci->top;
return NULL;
}
else { /* if is a C function, call it */
int n;
ci->savedpc = NULL;
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
lua_unlock(L);
#if LUA_COMPATUPVALUES
lua_pushupvalues(L);
lua_pushupvalues(L);
#endif
n = (*clvalue(func)->c.f)(L); /* do the actual call */
lua_lock(L);
return L->top - n;
n = (*clvalue(func)->c.f)(L); /* do the actual call */
lua_lock(L);
return L->top - n;
}
}
@ -179,12 +219,60 @@ void luaD_poscall (lua_State *L, int wanted, StkId firstResult) {
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
StkId firstResult = luaD_precall(L, func);
if (firstResult == NULL) /* is a Lua function? */
firstResult = luaV_execute(L, &clvalue(func)->l, func+1); /* call it */
if (firstResult == NULL) { /* is a Lua function? */
firstResult = luaV_execute(L); /* call it */
if (firstResult == NULL) {
luaD_poscall(L, 0, L->top);
luaD_error(L, "attempt to `yield' across tag-method/C-call boundary");
}
}
luaD_poscall(L, nResults, firstResult);
}
LUA_API void lua_cobegin (lua_State *L, int nargs) {
StkId func;
lua_lock(L);
func = L->top - (nargs+1); /* coroutine main function */
if (luaD_precall(L, func) != NULL)
luaD_error(L, "coroutine started with a C function");
lua_unlock(L);
}
LUA_API void lua_resume (lua_State *L, lua_State *co) {
StkId firstResult;
lua_lock(L);
if (co->ci->savedpc == NULL) /* no activation record? */
luaD_error(L, "thread is dead - cannot be resumed");
lua_assert(co->errorJmp == NULL);
co->errorJmp = L->errorJmp;
firstResult = luaV_execute(co);
if (firstResult != NULL) /* `return'? */
luaD_poscall(co, LUA_MULTRET, firstResult); /* ends this coroutine */
else { /* `yield' */
int nresults = GETARG_C(*((co->ci-1)->savedpc - 1)) - 1;
luaD_poscall(co, nresults, co->top); /* complete it */
if (nresults >= 0) co->top = co->ci->top;
}
co->errorJmp = NULL;
lua_unlock(L);
}
LUA_API int lua_yield (lua_State *L, int nresults) {
CallInfo *ci;
int ibase;
lua_lock(L);
ci = L->ci - 1; /* call info of calling function */
if (ci->pc == NULL)
luaD_error(L, "cannot `yield' a C function");
ibase = L->top - ci->base;
lua_unlock(L);
return ibase;
}
/*
** Execute a protected call.
*/
@ -330,7 +418,13 @@ static void message (lua_State *L, const char *s) {
** Reports an error, and jumps up to the available recovery label
*/
void luaD_error (lua_State *L, const char *s) {
if (s) message(L, s);
if (s) {
if (L->ci->savedpc) { /* error in Lua function preamble? */
L->ci->savedpc = NULL; /* pretend function was already running */
L->ci->pc = NULL;
}
message(L, s);
}
luaD_breakrun(L, LUA_ERRRUN);
}

View File

@ -168,8 +168,8 @@ OP_TESTGE,/* A C test := (R(A) >= R/K(C)) */
OP_TESTT,/* A B test := R(B); if (test) R(A) := R(B) */
OP_TESTF,/* A B test := not R(B); if (test) R(A) := R(B) */
OP_CALL,/* A B C R(A), ... ,R(A+C-1) := R(A)(R(A+1), ... ,R(A+B))*/
OP_RETURN,/* A B return R(A), ... ,R(A+B-1) (see (3)) */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))*/
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see (3)) */
OP_FORPREP,/* A sBc */
OP_FORLOOP,/* A sBc */
@ -182,6 +182,9 @@ OP_SETLISTO,/* A Bc */
OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE /* A Bc R(A) := closure(KPROTO[Bc], R(A), ... ,R(A+n)) */
/*----------------------------------------------------------------------
pseudo-instructions (interruptions): cannot occur in regular code
------------------------------------------------------------------------*/
} OpCode;
@ -194,11 +197,11 @@ OP_CLOSURE /* A Bc R(A) := closure(KPROTO[Bc], R(A), ... ,R(A+n)) */
(1) In the current implementation there is no `test' variable;
instructions OP_TEST* and OP_CJMP must always occur together.
(2) In OP_CALL, if (B == NO_REG) then B = top. C is the number of returns,
and can be NO_REG. OP_CALL can set `top' to last_result+1, so
(2) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(3) In OP_RETURN, if (B == NO_REG) then return up to `top'
(3) In OP_RETURN, if (B == 0) then return up to `top'
===========================================================================*/
@ -220,6 +223,12 @@ extern const lu_byte luaP_opmodes[NUM_OPCODES];
#define testOpMode(m, b) (luaP_opmodes[m] & (1 << (b)))
/*
** constant instructions
*/
extern const Instruction luaP_yieldop;
/*
** opcode names (only included when compiled with LUA_OPNAMES)
*/

View File

@ -335,7 +335,7 @@ static void close_func (LexState *ls) {
FuncState *fs = ls->fs;
Proto *f = fs->f;
removelocalvars(ls, fs->nactloc, 0);
luaK_codeABC(fs, OP_RETURN, 0, 0, 0); /* final return */
luaK_codeABC(fs, OP_RETURN, 0, 1, 0); /* final return */
luaK_getlabel(fs); /* close eventual list of pending jumps */
lua_assert(G(L)->roottable == fs->h);
G(L)->roottable = fs->h->next;
@ -449,13 +449,13 @@ static void funcargs (LexState *ls, expdesc *f) {
lua_assert(f->k == VNONRELOC);
base = f->u.i.info; /* base register for call */
if (args.k == VCALL)
nparams = NO_REG; /* open call */
nparams = LUA_MULTRET; /* open call */
else {
if (args.k != VVOID)
luaK_exp2nextreg(fs, &args); /* close last argument */
nparams = fs->freereg - (base+1);
}
init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams, 1));
init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
fs->freereg = base+1; /* call remove function and arguments and leaves
(unless changed) one result */
}
@ -1136,7 +1136,7 @@ static void retstat (LexState *ls) {
if (e.k == VCALL) {
luaK_setcallreturns(fs, &e, LUA_MULTRET);
first = fs->nactloc;
nret = NO_REG; /* return all values */
nret = LUA_MULTRET; /* return all values */
}
else {
luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */
@ -1144,7 +1144,7 @@ static void retstat (LexState *ls) {
nret = fs->freereg - first; /* return all `active' values */
}
}
luaK_codeABC(fs, OP_RETURN, first, nret, 0);
luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
fs->freereg = fs->nactloc; /* removes all temp values */
}

View File

@ -104,6 +104,7 @@ LUA_API lua_State *lua_newthread (lua_State *OL, int stacksize) {
}
}
if (OL) lua_unlock(OL);
lua_userstateopen(L);
return L;
}

View File

@ -39,6 +39,10 @@
#define LUA_USERSTATE
#endif
#ifndef lua_userstateopen
#define lua_userstateopen(l)
#endif
struct lua_longjmp; /* defined in ldo.c */
@ -77,8 +81,8 @@ typedef struct CallInfo {
const Instruction *savedpc;
lua_Hook linehook;
StkId top; /* top for this function (when it's a Lua function) */
/* extra information for debugging */
const Instruction **pc;
/* extra information for line tracing */
int lastpc; /* last pc traced */
int line; /* current line */
int refi; /* current index in `lineinfo' */

17
lua.h
View File

@ -45,10 +45,6 @@
#define LUA_ERRMEM 4
#define LUA_ERRERR 5
/* weak-table modes */
#define LUA_WEAK_KEY 1
#define LUA_WEAK_VALUE 2
typedef struct lua_State lua_State;
@ -182,6 +178,14 @@ LUA_API int lua_loadbuffer (lua_State *L, const char *buff, size_t size,
LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size,
const char *name);
/*
** coroutine functions
*/
LUA_API void lua_cobegin (lua_State *L, int nargs);
LUA_API int lua_yield (lua_State *L, int nresults);
LUA_API void lua_resume (lua_State *L, lua_State *co);
/*
** Garbage-collection functions
*/
@ -203,9 +207,6 @@ LUA_API void lua_concat (lua_State *L, int n);
LUA_API void *lua_newuserdata (lua_State *L, size_t size);
LUA_API void lua_newuserdatabox (lua_State *L, void *u);
LUA_API void lua_setweakmode (lua_State *L, int mode);
LUA_API int lua_getweakmode (lua_State *L, int index);
/*
@ -242,7 +243,7 @@ LUA_API int lua_getweakmode (lua_State *L, int index);
** compatibility macros and functions
*/
LUA_API void lua_pushupvalues (lua_State *L);
LUA_API int lua_pushupvalues (lua_State *L);
#define lua_isnull lua_isnone

86
lvm.c
View File

@ -254,29 +254,6 @@ void luaV_strconc (lua_State *L, int total, StkId top) {
}
static void adjust_varargs (lua_State *L, StkId base, int nfixargs) {
int i;
Table *htab;
TObject n, nname;
StkId firstvar = base + nfixargs; /* position of first vararg */
if (L->top < firstvar) {
luaD_checkstack(L, firstvar - L->top);
while (L->top < firstvar)
setnilvalue(L->top++);
}
htab = luaH_new(L, 0, 0);
for (i=0; firstvar+i<L->top; i++)
luaH_setnum(L, htab, i+1, firstvar+i);
/* store counter in field `n' */
setnvalue(&n, i);
setsvalue(&nname, luaS_newliteral(L, "n"));
luaH_set(L, htab, &nname, &n);
L->top = firstvar; /* remove elements from the stack */
sethvalue(L->top, htab);
incr_top;
}
static void powOp (lua_State *L, StkId ra, StkId rb, StkId rc) {
const TObject *b = rb;
const TObject *c = rc;
@ -307,8 +284,8 @@ static void powOp (lua_State *L, StkId ra, StkId rb, StkId rc) {
#define RC(i) (base+GETARG_C(i))
#define RKC(i) ((GETARG_C(i) < MAXSTACK) ? \
base+GETARG_C(i) : \
cl->p->k+GETARG_C(i)-MAXSTACK)
#define KBc(i) (cl->p->k+GETARG_Bc(i))
k+GETARG_C(i)-MAXSTACK)
#define KBc(i) (k+GETARG_Bc(i))
#define Arith(op, optm) { \
const TObject *b = RB(i); const TObject *c = RKC(i); \
@ -321,38 +298,26 @@ static void powOp (lua_State *L, StkId ra, StkId rb, StkId rc) {
}
#define luaV_poscall(L,c,f,ci) \
if (c != NO_REG) { \
luaD_poscall(L, c, f); \
L->top = ci->top; \
} \
else { \
luaD_poscall(L, LUA_MULTRET, f); \
}
#define dojump(pc, i) ((pc) += GETARG_sBc(i))
/*
** Executes the given Lua function. Parameters are between [base,top).
** Executes current Lua function. Parameters are between [base,top).
** Returns n such that the results are between [n,top).
*/
StkId luaV_execute (lua_State *L, const LClosure *cl, StkId base) {
StkId luaV_execute (lua_State *L) {
StkId base;
LClosure *cl;
TObject *k;
const Instruction *pc;
lua_Hook linehook;
reinit:
lua_assert(L->ci->savedpc == NULL);
base = L->ci->base;
cl = &clvalue(base - 1)->l;
k = cl->p->k;
linehook = L->ci->linehook;
L->ci->pc = &pc;
L->ci->top = base + cl->p->maxstacksize;
if (cl->p->is_vararg) /* varargs? */
adjust_varargs(L, base, cl->p->numparams);
if (base > L->stack_last - cl->p->maxstacksize)
luaD_stackerror(L);
while (L->top < L->ci->top)
setnilvalue(L->top++);
L->top = L->ci->top;
linehook = L->ci->linehook = L->linehook;
pc = cl->p->code;
pc = L->ci->savedpc;
L->ci->savedpc = NULL;
/* main loop of interpreter */
for (;;) {
const Instruction i = *pc++;
@ -535,18 +500,21 @@ StkId luaV_execute (lua_State *L, const LClosure *cl, StkId base) {
case OP_CALL: {
StkId firstResult;
int b = GETARG_B(i);
if (b != NO_REG) L->top = ra+b+1;
/* else previous instruction set top */
int nresults;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
nresults = GETARG_C(i) - 1;
firstResult = luaD_precall(L, ra);
if (firstResult) {
if (firstResult == base) { /* yield?? */
(L->ci-1)->savedpc = pc;
return NULL;
}
/* it was a C function (`precall' called it); adjust results */
luaV_poscall(L, GETARG_C(i), firstResult, L->ci);
luaD_poscall(L, nresults, firstResult);
if (nresults >= 0) L->top = L->ci->top;
}
else { /* it is a Lua function: `call' it */
CallInfo *ci = L->ci;
(ci-1)->savedpc = pc;
base = ci->base;
cl = &clvalue(base - 1)->l;
(L->ci-1)->savedpc = pc;
goto reinit;
}
break;
@ -556,19 +524,23 @@ StkId luaV_execute (lua_State *L, const LClosure *cl, StkId base) {
int b;
if (L->openupval) luaF_close(L, base);
b = GETARG_B(i);
if (b != NO_REG) L->top = ra+b;
if (b != 0) L->top = ra+b-1;
ci = L->ci - 1;
if (ci->savedpc == NULL)
return ra;
else { /* previous function is Lua: continue its execution */
int nresults;
lua_assert(ttype(ci->base-1) == LUA_TFUNCTION);
base = ci->base; /* restore previous values */
linehook = ci->linehook;
cl = &clvalue(base - 1)->l;
k = cl->p->k;
pc = ci->savedpc;
ci->savedpc = NULL;
lua_assert(GET_OPCODE(*(pc-1)) == OP_CALL);
luaV_poscall(L, GETARG_C(*(pc-1)), ra, ci);
nresults = GETARG_C(*(pc-1)) - 1;
luaD_poscall(L, nresults, ra);
if (nresults >= 0) L->top = L->ci->top;
}
break;
}

2
lvm.h
View File

@ -20,7 +20,7 @@ const TObject *luaV_tonumber (const TObject *obj, TObject *n);
int luaV_tostring (lua_State *L, TObject *obj);
void luaV_gettable (lua_State *L, StkId t, TObject *key, StkId res);
void luaV_settable (lua_State *L, StkId t, TObject *key, StkId val);
StkId luaV_execute (lua_State *L, const LClosure *cl, StkId base);
StkId luaV_execute (lua_State *L);
int luaV_lessthan (lua_State *L, const TObject *l, const TObject *r);
void luaV_strconc (lua_State *L, int total, StkId top);