mirror of
https://github.com/lua/lua.git
synced 2024-11-27 12:13:38 +08:00
Revamp around 'L->nCcalls' count
The field 'L->nCcalls' now counts downwards, so that the C-stack limits do not depend on the stack size.
This commit is contained in:
parent
d2a9b4ffb8
commit
3cd9b56ae6
13
ldo.c
13
ldo.c
@ -139,9 +139,8 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
|
||||
|
||||
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
l_uint32 oldnCcalls = L->nCcalls - L->nci;
|
||||
l_uint32 oldnCcalls = L->nCcalls + L->nci;
|
||||
struct lua_longjmp lj;
|
||||
lua_assert(L->nCcalls >= L->nci);
|
||||
lj.status = LUA_OK;
|
||||
lj.previous = L->errorJmp; /* chain new error handler */
|
||||
L->errorJmp = &lj;
|
||||
@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
(*f)(L, ud);
|
||||
);
|
||||
L->errorJmp = lj.previous; /* restore old error handler */
|
||||
L->nCcalls = oldnCcalls + L->nci;
|
||||
L->nCcalls = oldnCcalls - L->nci;
|
||||
return lj.status;
|
||||
}
|
||||
|
||||
@ -521,7 +520,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
incXCcalls(L);
|
||||
if (getCcalls(L) >= LUAI_MAXCSTACK) /* possible stack overflow? */
|
||||
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
|
||||
luaE_freeCI(L);
|
||||
luaD_call(L, func, nResults);
|
||||
decXCcalls(L);
|
||||
@ -672,10 +671,10 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
if (from == NULL)
|
||||
L->nCcalls = 1;
|
||||
L->nCcalls = LUAI_MAXCSTACK;
|
||||
else /* correct 'nCcalls' for this thread */
|
||||
L->nCcalls = getCcalls(from) - from->nci + L->nci + CSTACKCF;
|
||||
if (L->nCcalls >= LUAI_MAXCSTACK)
|
||||
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
|
||||
if (L->nCcalls <= CSTACKERR)
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
luai_userstateresume(L, nargs);
|
||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||
|
53
lstate.c
53
lstate.c
@ -97,35 +97,34 @@ void luaE_setdebt (global_State *g, l_mem debt) {
|
||||
|
||||
|
||||
/*
|
||||
** Increment count of "C calls" and check for overflows. In case of
|
||||
** Decrement count of "C calls" and check for overflows. In case of
|
||||
** a stack overflow, check appropriate error ("regular" overflow or
|
||||
** overflow while handling stack overflow).
|
||||
** If 'nCcalls' is larger than LUAI_MAXCSTACK but smaller than
|
||||
** LUAI_MAXCSTACK + CSTACKCF (plus 2 to avoid by-one errors), it means
|
||||
** it has just entered the "overflow zone", so the function raises an
|
||||
** overflow error.
|
||||
** If 'nCcalls' is larger than LUAI_MAXCSTACK + CSTACKCF + 2
|
||||
** (which means it is already handling an overflow) but smaller than
|
||||
** 9/8 of LUAI_MAXCSTACK, does not report an error (to allow message
|
||||
** handling to work).
|
||||
** Otherwise, report a stack overflow while handling a stack overflow
|
||||
** (probably caused by a repeating error in the message handling
|
||||
** function).
|
||||
** overflow while handling stack overflow). If 'nCcalls' is smaller
|
||||
** than CSTACKERR but larger than CSTACKMARK, it means it has just
|
||||
** entered the "overflow zone", so the function raises an overflow
|
||||
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
|
||||
** already handling an overflow) but larger than CSTACKERRMARK, does
|
||||
** not report an error (to allow message handling to work). Otherwise,
|
||||
** report a stack overflow while handling a stack overflow (probably
|
||||
** caused by a repeating error in the message handling function).
|
||||
*/
|
||||
|
||||
void luaE_enterCcall (lua_State *L) {
|
||||
int ncalls = getCcalls(L);
|
||||
L->nCcalls++;
|
||||
if (ncalls >= LUAI_MAXCSTACK) { /* possible overflow? */
|
||||
L->nCcalls--;
|
||||
if (ncalls <= CSTACKERR) { /* possible overflow? */
|
||||
luaE_freeCI(L); /* release unused CIs */
|
||||
ncalls = getCcalls(L); /* update call count */
|
||||
if (ncalls >= LUAI_MAXCSTACK) { /* still overflow? */
|
||||
if (ncalls <= LUAI_MAXCSTACK + CSTACKCF + 2) {
|
||||
/* no error before increments; raise the error now */
|
||||
L->nCcalls += (CSTACKCF + 4); /* avoid raising it again */
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
}
|
||||
else if (ncalls >= (LUAI_MAXCSTACK + (LUAI_MAXCSTACK >> 3)))
|
||||
if (ncalls <= CSTACKERR) { /* still overflow? */
|
||||
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
else if (ncalls >= CSTACKMARK) {
|
||||
/* not in error-handling zone; raise the error now */
|
||||
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
|
||||
luaG_runerror(L, "C stack overflow1");
|
||||
}
|
||||
/* else stack is in the error-handling zone;
|
||||
allow message handler to work */
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,13 +152,13 @@ void luaE_freeCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next = ci->next;
|
||||
ci->next = NULL;
|
||||
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
while ((ci = next) != NULL) {
|
||||
next = ci->next;
|
||||
luaM_free(L, ci);
|
||||
L->nci--;
|
||||
}
|
||||
L->nCcalls += L->nci; /* adjust result */
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
@ -169,7 +168,7 @@ void luaE_freeCI (lua_State *L) {
|
||||
void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next2; /* next's next */
|
||||
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
/* while there are two nexts */
|
||||
while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
|
||||
luaM_free(L, ci->next); /* free next */
|
||||
@ -178,7 +177,7 @@ void luaE_shrinkCI (lua_State *L) {
|
||||
next2->previous = ci;
|
||||
ci = next2; /* keep next's next */
|
||||
}
|
||||
L->nCcalls += L->nci; /* adjust result */
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
@ -264,7 +263,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
L->stacksize = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
L->errorJmp = NULL;
|
||||
L->nCcalls = 0;
|
||||
L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
|
||||
L->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
L->basehookcount = 0;
|
||||
|
51
lstate.h
51
lstate.h
@ -64,28 +64,45 @@
|
||||
|
||||
/*
|
||||
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
||||
** how many "C calls" it can do in the C stack, to avoid C-stack overflow.
|
||||
** This count is very rough approximation; it considers only recursive
|
||||
** functions inside the interpreter, as non-recursive calls can be
|
||||
** considered using a fixed (although unknown) amount of stack space.
|
||||
** how many "C calls" it still can do in the C stack, to avoid C-stack
|
||||
** overflow. This count is very rough approximation; it considers only
|
||||
** recursive functions inside the interpreter, as non-recursive calls
|
||||
** can be considered using a fixed (although unknown) amount of stack
|
||||
** space.
|
||||
**
|
||||
** The count itself has two parts: the lower part is the count itself;
|
||||
** the higher part counts the number of non-yieldable calls in the stack.
|
||||
** 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 of unkown amount
|
||||
** of space (e.g., functions using an auxiliary buffer), calls
|
||||
** to these functions add more than one to the count.
|
||||
** to these functions add more than one to the count (see CSTACKCF).
|
||||
**
|
||||
** The proper count also includes 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
|
||||
** increment nor to check 'nCcalls', as its use of C stack is already
|
||||
** accounted for.
|
||||
** 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)
|
||||
|
||||
|
||||
/* true if this thread does not have non-yieldable calls in the stack */
|
||||
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
||||
|
||||
@ -99,11 +116,11 @@
|
||||
/* Decrement the number of non-yieldable calls */
|
||||
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
||||
|
||||
/* Increment the number of non-yieldable calls and nCcalls */
|
||||
#define incXCcalls(L) ((L)->nCcalls += 0x10000 + CSTACKCF)
|
||||
/* Increment the number of non-yieldable calls and decrement nCcalls */
|
||||
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
|
||||
|
||||
/* Decrement the number of non-yieldable calls and nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 + CSTACKCF)
|
||||
/* Decrement the number of non-yieldable calls and increment nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
|
||||
|
||||
|
||||
|
||||
@ -336,7 +353,7 @@ LUAI_FUNC void luaE_enterCcall (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls--)
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -95,6 +95,8 @@ local function F (m)
|
||||
end
|
||||
end
|
||||
|
||||
local Cstacklevel
|
||||
|
||||
local showmem
|
||||
if not T then
|
||||
local max = 0
|
||||
@ -104,6 +106,7 @@ if not T then
|
||||
print(format(" ---- total memory: %s, max memory: %s ----\n",
|
||||
F(m), F(max)))
|
||||
end
|
||||
Cstacklevel = function () return 0 end -- no info about stack level
|
||||
else
|
||||
showmem = function ()
|
||||
T.checkmemory()
|
||||
@ -117,9 +120,16 @@ else
|
||||
T.totalmem"string", T.totalmem"table", T.totalmem"function",
|
||||
T.totalmem"userdata", T.totalmem"thread"))
|
||||
end
|
||||
|
||||
Cstacklevel = function ()
|
||||
local _, _, ncalls, nci = T.stacklevel()
|
||||
return ncalls + nci -- number of free slots in the C stack
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local Cstack = Cstacklevel()
|
||||
|
||||
--
|
||||
-- redefine dofile to run files through dump/undump
|
||||
--
|
||||
@ -211,6 +221,10 @@ debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
|
||||
-- to survive outside block
|
||||
_G.showmem = showmem
|
||||
|
||||
|
||||
assert(Cstack == Cstacklevel(),
|
||||
"should be at the same C-stack level it was when started the tests")
|
||||
|
||||
end --)
|
||||
|
||||
local _G, showmem, print, format, clock, time, difftime,
|
||||
|
Loading…
Reference in New Issue
Block a user