mirror of https://github.com/rusefi/lua.git
Revision of stackless implementation
- more organized handling of 'nCcalls' - comments - deprecation of 'setcstacklimit'
This commit is contained in:
parent
5d8ce05b3f
commit
287b302acb
2
all
2
all
|
@ -1,7 +1,7 @@
|
||||||
make -s -j
|
make -s -j
|
||||||
cd testes/libs; make -s
|
cd testes/libs; make -s
|
||||||
cd .. # back to directory 'testes'
|
cd .. # back to directory 'testes'
|
||||||
ulimit -S -s 2000
|
ulimit -S -s 1000
|
||||||
if { ../lua -W all.lua; } then
|
if { ../lua -W all.lua; } then
|
||||||
echo -e "\n\n final OK!!!!\n\n"
|
echo -e "\n\n final OK!!!!\n\n"
|
||||||
else
|
else
|
||||||
|
|
5
ldblib.c
5
ldblib.c
|
@ -440,10 +440,7 @@ static int db_traceback (lua_State *L) {
|
||||||
static int db_setcstacklimit (lua_State *L) {
|
static int db_setcstacklimit (lua_State *L) {
|
||||||
int limit = (int)luaL_checkinteger(L, 1);
|
int limit = (int)luaL_checkinteger(L, 1);
|
||||||
int res = lua_setcstacklimit(L, limit);
|
int res = lua_setcstacklimit(L, limit);
|
||||||
if (res == 0)
|
lua_pushinteger(L, res);
|
||||||
lua_pushboolean(L, 0);
|
|
||||||
else
|
|
||||||
lua_pushinteger(L, res);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
52
ldo.c
52
ldo.c
|
@ -448,10 +448,11 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Call a function (C or Lua). The function to be called is at *func.
|
** Prepares the call to a function (C or Lua). For C functions, also do
|
||||||
** The arguments are on the stack, right after the function.
|
** the call. The function to be called is at '*func'. The arguments are
|
||||||
** When returns, all the results are on the stack, starting at the original
|
** on the stack, right after the function. Returns true if the call was
|
||||||
** function position.
|
** made (it was a C function). When returns true, all the results are
|
||||||
|
** on the stack, starting at the original function position.
|
||||||
*/
|
*/
|
||||||
int luaD_precall (lua_State *L, StkId func, int nresults) {
|
int luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||||
lua_CFunction f;
|
lua_CFunction f;
|
||||||
|
@ -511,32 +512,34 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void stackerror (lua_State *L) {
|
/*
|
||||||
if (getCcalls(L) == LUAI_MAXCCALLS)
|
** Call a function (C or Lua). 'inc' can be 1 (increment number
|
||||||
luaG_runerror(L, "C stack overflow");
|
** of recursive invocations in the C stack) or nyci (the same plus
|
||||||
else if (getCcalls(L) >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
|
** increment number of non-yieldable calls).
|
||||||
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
*/
|
||||||
}
|
static void docall (lua_State *L, StkId func, int nResults, int inc) {
|
||||||
|
L->nCcalls += inc;
|
||||||
|
|
||||||
void luaD_call (lua_State *L, StkId func, int nResults) {
|
|
||||||
L->nCcalls++;
|
|
||||||
if (getCcalls(L) >= LUAI_MAXCCALLS)
|
if (getCcalls(L) >= LUAI_MAXCCALLS)
|
||||||
stackerror(L);
|
luaE_checkcstack(L);
|
||||||
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
||||||
luaV_execute(L, L->ci); /* call it */
|
luaV_execute(L, L->ci); /* call it */
|
||||||
L->nCcalls--;
|
L->nCcalls -= inc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** External interface for 'docall'
|
||||||
|
*/
|
||||||
|
void luaD_call (lua_State *L, StkId func, int nResults) {
|
||||||
|
return docall(L, func, nResults, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Similar to 'luaD_call', but does not allow yields during the call.
|
** Similar to 'luaD_call', but does not allow yields during the call.
|
||||||
*/
|
*/
|
||||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||||
incnny(L);
|
return docall(L, func, nResults, nyci);
|
||||||
luaD_call(L, func, nResults);
|
|
||||||
decnny(L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -650,13 +653,12 @@ static void resume (lua_State *L, void *ud) {
|
||||||
int n = *(cast(int*, ud)); /* number of arguments */
|
int n = *(cast(int*, ud)); /* number of arguments */
|
||||||
StkId firstArg = L->top - n; /* first argument */
|
StkId firstArg = L->top - n; /* first argument */
|
||||||
CallInfo *ci = L->ci;
|
CallInfo *ci = L->ci;
|
||||||
if (L->status == LUA_OK) { /* starting a coroutine? */
|
if (L->status == LUA_OK) /* starting a coroutine? */
|
||||||
if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */
|
docall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */
|
||||||
luaV_execute(L, L->ci); /* call it */
|
|
||||||
}
|
|
||||||
else { /* resuming from previous yield */
|
else { /* resuming from previous yield */
|
||||||
lua_assert(L->status == LUA_YIELD);
|
lua_assert(L->status == LUA_YIELD);
|
||||||
L->status = LUA_OK; /* mark that it is running (again) */
|
L->status = LUA_OK; /* mark that it is running (again) */
|
||||||
|
luaE_incCstack(L); /* control the C stack */
|
||||||
if (isLua(ci)) /* yielded inside a hook? */
|
if (isLua(ci)) /* yielded inside a hook? */
|
||||||
luaV_execute(L, ci); /* just continue running Lua code */
|
luaV_execute(L, ci); /* just continue running Lua code */
|
||||||
else { /* 'common' yield */
|
else { /* 'common' yield */
|
||||||
|
@ -684,9 +686,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||||
}
|
}
|
||||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||||
L->nCcalls = (from) ? getCcalls(from) + 1 : 1;
|
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||||
if (getCcalls(L) >= LUAI_MAXCCALLS)
|
|
||||||
return resume_error(L, "C stack overflow", nargs);
|
|
||||||
luai_userstateresume(L, nargs);
|
luai_userstateresume(L, nargs);
|
||||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||||
status = luaD_rawrunprotected(L, resume, &nargs);
|
status = luaD_rawrunprotected(L, resume, &nargs);
|
||||||
|
|
11
llimits.h
11
llimits.h
|
@ -234,6 +234,17 @@ typedef l_uint32 Instruction;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Maximum depth for nested C calls, syntactical nested non-terminals,
|
||||||
|
** and other features implemented through recursion in C. (Value must
|
||||||
|
** fit in a 16-bit unsigned integer. It must also be compatible with
|
||||||
|
** the size of the C stack.)
|
||||||
|
*/
|
||||||
|
#if !defined(LUAI_MAXCCALLS)
|
||||||
|
#define LUAI_MAXCCALLS 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** macros that are executed whenever program enters the Lua core
|
** macros that are executed whenever program enters the Lua core
|
||||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||||
|
|
|
@ -489,11 +489,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void enterlevel (LexState *ls) {
|
#define enterlevel(ls) luaE_incCstack(ls->L)
|
||||||
lua_State *L = ls->L;
|
|
||||||
L->nCcalls++;
|
|
||||||
checklimit(ls->fs, getCcalls(L), LUAI_MAXCCALLS, "C levels");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define leavelevel(ls) ((ls)->L->nCcalls--)
|
#define leavelevel(ls) ((ls)->L->nCcalls--)
|
||||||
|
|
45
lstate.c
45
lstate.c
|
@ -97,25 +97,8 @@ void luaE_setdebt (global_State *g, l_mem debt) {
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
||||||
global_State *g = G(L);
|
UNUSED(L); UNUSED(limit);
|
||||||
int ccalls;
|
return LUAI_MAXCCALLS; /* warning?? */
|
||||||
luaE_freeCI(L); /* release unused CIs */
|
|
||||||
ccalls = getCcalls(L);
|
|
||||||
if (limit >= 40000)
|
|
||||||
return 0; /* out of bounds */
|
|
||||||
limit += CSTACKERR;
|
|
||||||
if (L != g-> mainthread)
|
|
||||||
return 0; /* only main thread can change the C stack */
|
|
||||||
else if (ccalls <= CSTACKERR)
|
|
||||||
return 0; /* handling overflow */
|
|
||||||
else {
|
|
||||||
int diff = limit - g->Cstacklimit;
|
|
||||||
if (ccalls + diff <= CSTACKERR)
|
|
||||||
return 0; /* new limit would cause an overflow */
|
|
||||||
g->Cstacklimit = limit; /* set new limit */
|
|
||||||
L->nCcalls += diff; /* correct 'nCcalls' */
|
|
||||||
return limit - diff - CSTACKERR; /* success; return previous limit */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,6 +155,28 @@ void luaE_shrinkCI (lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
|
||||||
|
** If equal, raises an overflow error. If value is larger than
|
||||||
|
** LUAI_MAXCCALLS (which means it is handling an overflow) but
|
||||||
|
** not much larger, does not report an error (to allow overflow
|
||||||
|
** handling to work).
|
||||||
|
*/
|
||||||
|
void luaE_checkcstack (lua_State *L) {
|
||||||
|
if (getCcalls(L) == LUAI_MAXCCALLS)
|
||||||
|
luaG_runerror(L, "C stack overflow");
|
||||||
|
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
|
||||||
|
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
||||||
|
L->nCcalls++;
|
||||||
|
if (getCcalls(L) >= LUAI_MAXCCALLS)
|
||||||
|
luaE_checkcstack(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void stack_init (lua_State *L1, lua_State *L) {
|
static void stack_init (lua_State *L1, lua_State *L) {
|
||||||
int i; CallInfo *ci;
|
int i; CallInfo *ci;
|
||||||
/* initialize stack array */
|
/* initialize stack array */
|
||||||
|
@ -357,7 +362,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||||
preinit_thread(L, g);
|
preinit_thread(L, g);
|
||||||
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
||||||
L->next = NULL;
|
L->next = NULL;
|
||||||
g->Cstacklimit = L->nCcalls = 0;
|
L->nCcalls = 0;
|
||||||
incnny(L); /* main thread is always non yieldable */
|
incnny(L); /* main thread is always non yieldable */
|
||||||
g->frealloc = f;
|
g->frealloc = f;
|
||||||
g->ud = ud;
|
g->ud = ud;
|
||||||
|
|
56
lstate.h
56
lstate.h
|
@ -87,49 +87,13 @@
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
** About 'nCcalls': This count has two parts: the lower 16 bits counts
|
||||||
** how many "C calls" it still can do in the C stack, to avoid C-stack
|
** the number of recursive invocations in the C stack; the higher
|
||||||
** overflow. This count is very rough approximation; it considers only
|
** 16 bits counts the number of non-yieldable calls in the stack.
|
||||||
** recursive functions inside the interpreter, as non-recursive calls
|
** (They are together so that we can change and save both with one
|
||||||
** can be considered using a fixed (although unknown) amount of stack
|
** instruction.)
|
||||||
** space.
|
|
||||||
**
|
|
||||||
** The count has two parts: the lower part is the count itself; the
|
|
||||||
** higher part counts the number of non-yieldable calls in the stack.
|
|
||||||
** (They are together so that we can change both with one instruction.)
|
|
||||||
**
|
|
||||||
** Because calls to external C functions can use an unknown amount
|
|
||||||
** of space (e.g., functions using an auxiliary buffer), calls
|
|
||||||
** to these functions add more than one to the count (see CSTACKCF).
|
|
||||||
**
|
|
||||||
** The proper count excludes the number of CallInfo structures allocated
|
|
||||||
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
|
|
||||||
** (and "consumes" one CallInfo), it needs neither to decrement nor to
|
|
||||||
** check 'nCcalls', as its use of C stack is already accounted for.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* number of "C stack slots" used by an external C function */
|
|
||||||
#define CSTACKCF 10
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The C-stack size is sliced in the following zones:
|
|
||||||
** - larger than CSTACKERR: normal stack;
|
|
||||||
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
|
|
||||||
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
|
|
||||||
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
|
|
||||||
** (Because the counter can be decremented CSTACKCF at once, we need
|
|
||||||
** the so called "buffer zones", with at least that size, to properly
|
|
||||||
** detect a change from one zone to the next.)
|
|
||||||
*/
|
|
||||||
#define CSTACKERR (8 * CSTACKCF)
|
|
||||||
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
|
|
||||||
#define CSTACKERRMARK (CSTACKCF + 2)
|
|
||||||
|
|
||||||
|
|
||||||
/* initial limit for the C-stack of threads */
|
|
||||||
#define CSTACKTHREAD (2 * CSTACKERR)
|
|
||||||
|
|
||||||
|
|
||||||
/* true if this thread does not have non-yieldable calls in the stack */
|
/* true if this thread does not have non-yieldable calls in the stack */
|
||||||
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
||||||
|
@ -144,7 +108,8 @@
|
||||||
/* Decrement the number of non-yieldable calls */
|
/* Decrement the number of non-yieldable calls */
|
||||||
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
||||||
|
|
||||||
|
/* Non-yieldable call increment */
|
||||||
|
#define nyci (0x10000 | 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -290,7 +255,6 @@ typedef struct global_State {
|
||||||
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
||||||
lua_WarnFunction warnf; /* warning function */
|
lua_WarnFunction warnf; /* warning function */
|
||||||
void *ud_warn; /* auxiliary data to 'warnf' */
|
void *ud_warn; /* auxiliary data to 'warnf' */
|
||||||
unsigned int Cstacklimit; /* current limit for the C stack */
|
|
||||||
} global_State;
|
} global_State;
|
||||||
|
|
||||||
|
|
||||||
|
@ -314,7 +278,7 @@ struct lua_State {
|
||||||
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
||||||
volatile lua_Hook hook;
|
volatile lua_Hook hook;
|
||||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||||
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
|
||||||
int oldpc; /* last pc traced */
|
int oldpc; /* last pc traced */
|
||||||
int stacksize;
|
int stacksize;
|
||||||
int basehookcount;
|
int basehookcount;
|
||||||
|
@ -383,11 +347,11 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
|
||||||
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
||||||
LUAI_FUNC void luaE_freeCI (lua_State *L);
|
LUAI_FUNC void luaE_freeCI (lua_State *L);
|
||||||
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
||||||
|
LUAI_FUNC void luaE_checkcstack (lua_State *L);
|
||||||
|
LUAI_FUNC void luaE_incCstack (lua_State *L);
|
||||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||||
|
|
||||||
|
|
||||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
15
luaconf.h
15
luaconf.h
|
@ -36,21 +36,6 @@
|
||||||
** =====================================================================
|
** =====================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* >>> move back to llimits.h
|
|
||||||
@@ LUAI_MAXCCALLS defines the maximum depth for nested calls and
|
|
||||||
** also limits the maximum depth of other recursive algorithms in
|
|
||||||
** the implementation, such as syntactic analysis. A value too
|
|
||||||
** large may allow the interpreter to crash (C-stack overflow).
|
|
||||||
** The default value seems ok for regular machines, but may be
|
|
||||||
** too high for restricted hardware.
|
|
||||||
** The test file 'cstack.lua' may help finding a good limit.
|
|
||||||
** (It will crash with a limit too high.)
|
|
||||||
*/
|
|
||||||
#if !defined(LUAI_MAXCCALLS)
|
|
||||||
#define LUAI_MAXCCALLS 200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
|
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
|
||||||
** Define it if you want Lua to avoid the use of a few C99 features
|
** Define it if you want Lua to avoid the use of a few C99 features
|
||||||
|
|
6
lvm.c
6
lvm.c
|
@ -1124,7 +1124,7 @@ void luaV_finishOp (lua_State *L) {
|
||||||
|
|
||||||
|
|
||||||
void luaV_execute (lua_State *L, CallInfo *ci) {
|
void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
const CallInfo *origci = ci;
|
CallInfo * const origci = ci;
|
||||||
LClosure *cl;
|
LClosure *cl;
|
||||||
TValue *k;
|
TValue *k;
|
||||||
StkId base;
|
StkId base;
|
||||||
|
@ -1624,7 +1624,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
vmcase(OP_TAILCALL) {
|
vmcase(OP_TAILCALL) {
|
||||||
int b = GETARG_B(i); /* number of arguments + 1 (function) */
|
int b = GETARG_B(i); /* number of arguments + 1 (function) */
|
||||||
int nparams1 = GETARG_C(i);
|
int nparams1 = GETARG_C(i);
|
||||||
/* delat is virtual 'func' - real 'func' (vararg functions) */
|
/* delta is virtual 'func' - real 'func' (vararg functions) */
|
||||||
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
|
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
|
||||||
if (b != 0)
|
if (b != 0)
|
||||||
L->top = ra + b;
|
L->top = ra + b;
|
||||||
|
@ -1648,7 +1648,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
updatetrap(ci);
|
updatetrap(ci);
|
||||||
updatestack(ci); /* stack may have been relocated */
|
updatestack(ci); /* stack may have been relocated */
|
||||||
ci->func -= delta;
|
ci->func -= delta;
|
||||||
luaD_poscall(L, ci, cast_int(L->top - ra));
|
luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
ci->func -= delta;
|
ci->func -= delta;
|
||||||
|
|
|
@ -2436,8 +2436,16 @@ When you interact with the Lua API,
|
||||||
you are responsible for ensuring consistency.
|
you are responsible for ensuring consistency.
|
||||||
In particular,
|
In particular,
|
||||||
@emph{you are responsible for controlling stack overflow}.
|
@emph{you are responsible for controlling stack overflow}.
|
||||||
You can use the function @Lid{lua_checkstack}
|
When you call any API function,
|
||||||
to ensure that the stack has enough space for pushing new elements.
|
you must ensure the stack has enough room to accommodate the results.
|
||||||
|
|
||||||
|
There is one exception to the above rule:
|
||||||
|
When you call a Lua function
|
||||||
|
without a fixed number of results @seeF{lua_call},
|
||||||
|
Lua ensures that the stack has enough space for all results.
|
||||||
|
However, it does not ensure any extra space.
|
||||||
|
So, before pushing anything on the stack after such a call
|
||||||
|
you should use @Lid{lua_checkstack}.
|
||||||
|
|
||||||
Whenever Lua calls C,
|
Whenever Lua calls C,
|
||||||
it ensures that the stack has space for
|
it ensures that the stack has space for
|
||||||
|
@ -2446,13 +2454,9 @@ that is, you can safely push up to @id{LUA_MINSTACK} values into it.
|
||||||
@id{LUA_MINSTACK} is defined as 20,
|
@id{LUA_MINSTACK} is defined as 20,
|
||||||
so that usually you do not have to worry about stack space
|
so that usually you do not have to worry about stack space
|
||||||
unless your code has loops pushing elements onto the stack.
|
unless your code has loops pushing elements onto the stack.
|
||||||
|
Whenever necessary,
|
||||||
When you call a Lua function
|
you can use the function @Lid{lua_checkstack}
|
||||||
without a fixed number of results @seeF{lua_call},
|
to ensure that the stack has enough space for pushing new elements.
|
||||||
Lua ensures that the stack has enough space for all results,
|
|
||||||
but it does not ensure any extra space.
|
|
||||||
So, before pushing anything on the stack after such a call
|
|
||||||
you should use @Lid{lua_checkstack}.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2695,7 +2699,7 @@ Therefore, if a @N{C function} @id{foo} calls an API function
|
||||||
and this API function yields
|
and this API function yields
|
||||||
(directly or indirectly by calling another function that yields),
|
(directly or indirectly by calling another function that yields),
|
||||||
Lua cannot return to @id{foo} any more,
|
Lua cannot return to @id{foo} any more,
|
||||||
because the @id{longjmp} removes its frame from the C stack.
|
because the @id{longjmp} removes its frame from the @N{C stack}.
|
||||||
|
|
||||||
To avoid this kind of problem,
|
To avoid this kind of problem,
|
||||||
Lua raises an error whenever it tries to yield across an API call,
|
Lua raises an error whenever it tries to yield across an API call,
|
||||||
|
@ -2719,7 +2723,7 @@ After the thread resumes,
|
||||||
it eventually will finish running the callee function.
|
it eventually will finish running the callee function.
|
||||||
However,
|
However,
|
||||||
the callee function cannot return to the original function,
|
the callee function cannot return to the original function,
|
||||||
because its frame in the C stack was destroyed by the yield.
|
because its frame in the @N{C stack} was destroyed by the yield.
|
||||||
Instead, Lua calls a @def{continuation function},
|
Instead, Lua calls a @def{continuation function},
|
||||||
which was given as an argument to the callee function.
|
which was given as an argument to the callee function.
|
||||||
As the name implies,
|
As the name implies,
|
||||||
|
@ -2841,7 +2845,7 @@ and therefore may raise any errors.
|
||||||
|
|
||||||
Converts the @x{acceptable index} @id{idx}
|
Converts the @x{acceptable index} @id{idx}
|
||||||
into an equivalent @x{absolute index}
|
into an equivalent @x{absolute index}
|
||||||
(that is, one that does not depend on the stack top).
|
(that is, one that does not depend on the stack size).
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4340,7 +4344,7 @@ as if it was already marked.
|
||||||
Note that, both in case of errors and of a regular return,
|
Note that, both in case of errors and of a regular return,
|
||||||
by the time the @idx{__close} metamethod runs,
|
by the time the @idx{__close} metamethod runs,
|
||||||
the @N{C stack} was already unwound,
|
the @N{C stack} was already unwound,
|
||||||
so that any automatic C variable declared in the calling function
|
so that any automatic @N{C variable} declared in the calling function
|
||||||
will be out of scope.
|
will be out of scope.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4955,20 +4959,6 @@ calling @Lid{lua_yield} with @id{nresults} equal to zero
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@APIEntry{int (lua_setcstacklimit) (lua_State *L, unsigned int limit);|
|
|
||||||
@apii{0,0,-}
|
|
||||||
|
|
||||||
Sets a new limit for the C stack.
|
|
||||||
This limit controls how deeply nested calls can go in Lua,
|
|
||||||
with the intent of avoiding a stack overflow.
|
|
||||||
Returns the old limit in case of success,
|
|
||||||
or zero in case of error.
|
|
||||||
For more details about this function,
|
|
||||||
see @Lid{debug.setcstacklimit},
|
|
||||||
its equivalent in the standard library.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);|
|
@APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);|
|
||||||
@apii{0,0,-}
|
@apii{0,0,-}
|
||||||
|
|
||||||
|
@ -8756,34 +8746,6 @@ to the userdata @id{u} plus a boolean,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LibEntry{debug.setcstacklimit (limit)|
|
|
||||||
|
|
||||||
Sets a new limit for the C stack.
|
|
||||||
This limit controls how deeply nested calls can go in Lua,
|
|
||||||
with the intent of avoiding a stack overflow.
|
|
||||||
A limit too small restricts recursive calls pointlessly;
|
|
||||||
a limit too large exposes the interpreter to stack-overflow crashes.
|
|
||||||
Unfortunately, there is no way to know a priori
|
|
||||||
the maximum safe limit for a platform.
|
|
||||||
|
|
||||||
Each call made from Lua code counts one unit.
|
|
||||||
Other operations (e.g., calls made from C to Lua or resuming a coroutine)
|
|
||||||
may have a higher cost.
|
|
||||||
|
|
||||||
This function has the following restrictions:
|
|
||||||
@description{
|
|
||||||
@item{It can only be called from the main coroutine (thread);}
|
|
||||||
@item{It cannot be called while handling a stack-overflow error;}
|
|
||||||
@item{@id{limit} must be less than 40000;}
|
|
||||||
@item{@id{limit} cannot be less than the amount of C stack in use.}
|
|
||||||
}
|
|
||||||
If a call does not respect some restriction,
|
|
||||||
it returns a false value.
|
|
||||||
Otherwise,
|
|
||||||
the call returns the old limit.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
|
@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
|
||||||
|
|
||||||
Sets the given function as the debug hook.
|
Sets the given function as the debug hook.
|
||||||
|
|
|
@ -1,75 +1,29 @@
|
||||||
-- $Id: testes/cstack.lua $
|
-- $Id: testes/cstack.lua $
|
||||||
-- See Copyright Notice in file all.lua
|
-- See Copyright Notice in file all.lua
|
||||||
|
|
||||||
do return end
|
|
||||||
|
|
||||||
local debug = require "debug"
|
|
||||||
|
|
||||||
print"testing C-stack overflow detection"
|
print"testing C-stack overflow detection"
|
||||||
print"If this test crashes, see its file ('cstack.lua')"
|
|
||||||
|
|
||||||
-- Segmentation faults in these tests probably result from a C-stack
|
-- Segmentation faults in these tests probably result from a C-stack
|
||||||
-- overflow. To avoid these errors, you can use the function
|
-- overflow. To avoid these errors, you should set a smaller limit for
|
||||||
-- 'debug.setcstacklimit' to set a smaller limit for the use of
|
-- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
|
||||||
-- C stack by Lua. After finding a reliable limit, you might want
|
|
||||||
-- to recompile Lua with this limit as the value for
|
|
||||||
-- the constant 'LUAI_MAXCCALLS', which defines the default limit.
|
|
||||||
-- (The default limit is printed by this test.)
|
|
||||||
-- Alternatively, you can ensure a larger stack for the program.
|
-- Alternatively, you can ensure a larger stack for the program.
|
||||||
|
|
||||||
-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
|
|
||||||
-- higher than 2_000.
|
|
||||||
|
|
||||||
|
|
||||||
-- get and print original limit
|
|
||||||
local origlimit <const> = debug.setcstacklimit(400)
|
|
||||||
print("default stack limit: " .. origlimit)
|
|
||||||
|
|
||||||
|
|
||||||
-- Do the tests using the original limit. Or else you may want to change
|
|
||||||
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
|
|
||||||
-- values to check whether they are reliable.
|
|
||||||
local currentlimit <const> = origlimit
|
|
||||||
debug.setcstacklimit(currentlimit)
|
|
||||||
print("current stack limit: " .. currentlimit)
|
|
||||||
|
|
||||||
|
|
||||||
local function checkerror (msg, f, ...)
|
local function checkerror (msg, f, ...)
|
||||||
local s, err = pcall(f, ...)
|
local s, err = pcall(f, ...)
|
||||||
assert(not s and string.find(err, msg))
|
assert(not s and string.find(err, msg))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- auxiliary function to keep 'count' on the screen even if the program
|
|
||||||
-- crashes.
|
|
||||||
local count
|
|
||||||
local back = string.rep("\b", 8)
|
|
||||||
local function progress ()
|
|
||||||
count = count + 1
|
|
||||||
local n = string.format("%-8d", count)
|
|
||||||
io.stderr:write(back, n) -- erase previous value and write new one
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
do print("testing simple recursion:")
|
|
||||||
count = 0
|
|
||||||
local function foo ()
|
|
||||||
progress()
|
|
||||||
foo() -- do recursive calls until a stack error (or crash)
|
|
||||||
end
|
|
||||||
checkerror("stack overflow", foo)
|
|
||||||
print("\tfinal count: ", count)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
do print("testing stack overflow in message handling")
|
do print("testing stack overflow in message handling")
|
||||||
count = 0
|
local count = 0
|
||||||
local function loop (x, y, z)
|
local function loop (x, y, z)
|
||||||
progress()
|
count = count + 1
|
||||||
return 1 + loop(x, y, z)
|
return 1 + loop(x, y, z)
|
||||||
end
|
end
|
||||||
local res, msg = xpcall(loop, loop)
|
local res, msg = xpcall(loop, loop)
|
||||||
assert(msg == "error in error handling")
|
assert(msg == "error in error handling")
|
||||||
print("\tfinal count: ", count)
|
print("final count: ", count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,97 +36,66 @@ do print("testing recursion inside pattern matching")
|
||||||
end
|
end
|
||||||
local m = f(80)
|
local m = f(80)
|
||||||
assert(#m == 80)
|
assert(#m == 80)
|
||||||
checkerror("too complex", f, 200000)
|
checkerror("too complex", f, 2000)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do print("testing stack-overflow in recursive 'gsub'")
|
do print("testing stack-overflow in recursive 'gsub'")
|
||||||
count = 0
|
local count = 0
|
||||||
local function foo ()
|
local function foo ()
|
||||||
progress()
|
count = count + 1
|
||||||
string.gsub("a", ".", foo)
|
string.gsub("a", ".", foo)
|
||||||
end
|
end
|
||||||
checkerror("stack overflow", foo)
|
checkerror("stack overflow", foo)
|
||||||
print("\tfinal count: ", count)
|
print("final count: ", count)
|
||||||
|
|
||||||
print("testing stack-overflow in recursive 'gsub' with metatables")
|
print("testing stack-overflow in recursive 'gsub' with metatables")
|
||||||
count = 0
|
local count = 0
|
||||||
local t = setmetatable({}, {__index = foo})
|
local t = setmetatable({}, {__index = foo})
|
||||||
foo = function ()
|
foo = function ()
|
||||||
count = count + 1
|
count = count + 1
|
||||||
progress(count)
|
|
||||||
string.gsub("a", ".", t)
|
string.gsub("a", ".", t)
|
||||||
end
|
end
|
||||||
checkerror("stack overflow", foo)
|
checkerror("stack overflow", foo)
|
||||||
print("\tfinal count: ", count)
|
print("final count: ", count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- bug in 5.4.0
|
do -- bug in 5.4.0
|
||||||
print("testing limits in coroutines inside deep calls")
|
print("testing limits in coroutines inside deep calls")
|
||||||
count = 0
|
local count = 0
|
||||||
local lim = 1000
|
local lim = 1000
|
||||||
local function stack (n)
|
local function stack (n)
|
||||||
progress()
|
|
||||||
if n > 0 then return stack(n - 1) + 1
|
if n > 0 then return stack(n - 1) + 1
|
||||||
else coroutine.wrap(function ()
|
else coroutine.wrap(function ()
|
||||||
|
count = count + 1
|
||||||
stack(lim)
|
stack(lim)
|
||||||
end)()
|
end)()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print(xpcall(stack, function () return "ok" end, lim))
|
local st, msg = xpcall(stack, function () return "ok" end, lim)
|
||||||
|
assert(not st and msg == "ok")
|
||||||
|
print("final count: ", count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do print("testing changes in C-stack limit")
|
do
|
||||||
|
print("nesting of resuming yielded coroutines")
|
||||||
|
local count = 0
|
||||||
|
|
||||||
-- Just an alternative limit, different from the current one
|
local function body ()
|
||||||
-- (smaller to avoid stack overflows)
|
coroutine.yield()
|
||||||
local alterlimit <const> = currentlimit * 8 // 10
|
local f = coroutine.wrap(body)
|
||||||
|
f(); -- start new coroutine (will stop in previous yield)
|
||||||
assert(not debug.setcstacklimit(0)) -- limit too small
|
count = count + 1
|
||||||
assert(not debug.setcstacklimit(50000)) -- limit too large
|
f() -- call it recursively
|
||||||
local co = coroutine.wrap (function ()
|
|
||||||
return debug.setcstacklimit(alterlimit)
|
|
||||||
end)
|
|
||||||
assert(not co()) -- cannot change C stack inside coroutine
|
|
||||||
|
|
||||||
local n
|
|
||||||
local function foo () n = n + 1; foo () end
|
|
||||||
|
|
||||||
local function check ()
|
|
||||||
n = 0
|
|
||||||
pcall(foo)
|
|
||||||
return n
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set limit to 'alterlimit'
|
local f = coroutine.wrap(body)
|
||||||
assert(debug.setcstacklimit(alterlimit) == currentlimit)
|
f()
|
||||||
local limalter <const> = check()
|
assert(not pcall(f))
|
||||||
-- set a very low limit (given that there are already several active
|
print("final count: ", count)
|
||||||
-- calls to arrive here)
|
|
||||||
local lowlimit <const> = 38
|
|
||||||
assert(debug.setcstacklimit(lowlimit) == alterlimit)
|
|
||||||
-- usable limit is much lower, due to active calls
|
|
||||||
local actuallow = check()
|
|
||||||
assert(actuallow < lowlimit - 30)
|
|
||||||
-- now, add 'lowlimit' extra slots, which should all be available
|
|
||||||
assert(debug.setcstacklimit(lowlimit + lowlimit) == lowlimit)
|
|
||||||
local lim2 <const> = check()
|
|
||||||
assert(lim2 == actuallow + lowlimit)
|
|
||||||
|
|
||||||
|
|
||||||
-- 'setcstacklimit' works inside protected calls. (The new stack
|
|
||||||
-- limit is kept when 'pcall' returns.)
|
|
||||||
assert(pcall(function ()
|
|
||||||
assert(debug.setcstacklimit(alterlimit) == lowlimit * 2)
|
|
||||||
assert(check() <= limalter)
|
|
||||||
end))
|
|
||||||
|
|
||||||
assert(check() == limalter)
|
|
||||||
-- restore original limit
|
|
||||||
assert(debug.setcstacklimit(origlimit) == alterlimit)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
print'OK'
|
print'OK'
|
||||||
|
|
|
@ -532,7 +532,8 @@ local function testrep (init, rep, close, repc, finalresult)
|
||||||
end
|
end
|
||||||
s = init .. string.rep(rep, 500)
|
s = init .. string.rep(rep, 500)
|
||||||
local res, msg = load(s) -- 500 levels not ok
|
local res, msg = load(s) -- 500 levels not ok
|
||||||
assert(not res and string.find(msg, "too many"))
|
assert(not res and (string.find(msg, "too many") or
|
||||||
|
string.find(msg, "overflow")))
|
||||||
end
|
end
|
||||||
|
|
||||||
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
|
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
|
||||||
|
|
Loading…
Reference in New Issue