mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
Simpler handling of errors when creating tbc variables
New field 'lua_State.ptbc' keeps to-be-closed variable until its upvalue is created, so that it can be closed in case of a memory-allocation error.
This commit is contained in:
parent
d0f34d9137
commit
6ccd24eff5
1
ldo.c
1
ldo.c
@ -163,6 +163,7 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
|||||||
if (oldstack == newstack)
|
if (oldstack == newstack)
|
||||||
return; /* stack address did not change */
|
return; /* stack address did not change */
|
||||||
L->top = (L->top - oldstack) + newstack;
|
L->top = (L->top - oldstack) + newstack;
|
||||||
|
lua_assert(L->ptbc == NULL);
|
||||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||||
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
||||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||||
|
37
lfunc.c
37
lfunc.c
@ -155,32 +155,19 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Try to create a to-be-closed upvalue
|
** Create a to-be-closed upvalue. If there is a memory allocation error,
|
||||||
** (can raise a memory-allocation error)
|
** 'ptbc' keeps the object so it can be closed as soon as possible.
|
||||||
*/
|
** (Since memory errors have no handler, that will happen before any
|
||||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
** stack reallocation.)
|
||||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Create a to-be-closed upvalue. If there is a memory error
|
|
||||||
** when creating the upvalue, the closing method must be called here,
|
|
||||||
** as there is no upvalue to call it later.
|
|
||||||
*/
|
*/
|
||||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||||
TValue *obj = s2v(level);
|
TValue *obj = s2v(level);
|
||||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
||||||
int status;
|
|
||||||
checkclosemth(L, level, obj);
|
checkclosemth(L, level, obj);
|
||||||
status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
L->ptbc = level; /* in case of allocation error */
|
||||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
newupval(L, 1, level, &L->openupval);
|
||||||
lua_assert(status == LUA_ERRMEM);
|
L->ptbc = NULL; /* no errors */
|
||||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
|
||||||
callclosemethod(L, s2v(level), s2v(level + 1), 0);
|
|
||||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,11 +183,19 @@ void luaF_unlinkupval (UpVal *uv) {
|
|||||||
/*
|
/*
|
||||||
** Close all upvalues up to the given stack level. A 'status' equal
|
** Close all upvalues up to the given stack level. A 'status' equal
|
||||||
** to NOCLOSINGMETH closes upvalues without running any __close
|
** to NOCLOSINGMETH closes upvalues without running any __close
|
||||||
** metamethods.
|
** metamethods. If there is a pending to-be-closed value, close
|
||||||
|
** it before anything else.
|
||||||
*/
|
*/
|
||||||
void luaF_close (lua_State *L, StkId level, int status, int yy) {
|
void luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||||
UpVal *uv;
|
UpVal *uv;
|
||||||
StkId upl; /* stack index pointed by 'uv' */
|
StkId upl; /* stack index pointed by 'uv' */
|
||||||
|
if (unlikely(status == LUA_ERRMEM && L->ptbc != NULL)) {
|
||||||
|
upl = L->ptbc;
|
||||||
|
L->ptbc = NULL; /* remove from "list" before closing */
|
||||||
|
prepcallclosemth(L, upl, status, yy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lua_assert(L->ptbc == NULL); /* must be empty for other status */
|
||||||
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
||||||
TValue *slot = &uv->u.value; /* new position for value */
|
TValue *slot = &uv->u.value; /* new position for value */
|
||||||
lua_assert(uplevel(uv) < L->top);
|
lua_assert(uplevel(uv) < L->top);
|
||||||
|
4
lstate.c
4
lstate.c
@ -253,6 +253,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
|||||||
L->ci = NULL;
|
L->ci = NULL;
|
||||||
L->nci = 0;
|
L->nci = 0;
|
||||||
L->twups = L; /* thread has no upvalues */
|
L->twups = L; /* thread has no upvalues */
|
||||||
|
L->nCcalls = 0;
|
||||||
L->errorJmp = NULL;
|
L->errorJmp = NULL;
|
||||||
L->hook = NULL;
|
L->hook = NULL;
|
||||||
L->hookmask = 0;
|
L->hookmask = 0;
|
||||||
@ -263,6 +264,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
|||||||
L->status = LUA_OK;
|
L->status = LUA_OK;
|
||||||
L->errfunc = 0;
|
L->errfunc = 0;
|
||||||
L->oldpc = 0;
|
L->oldpc = 0;
|
||||||
|
L->ptbc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -296,7 +298,6 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
|||||||
setthvalue2s(L, L->top, L1);
|
setthvalue2s(L, L->top, L1);
|
||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
preinit_thread(L1, g);
|
preinit_thread(L1, g);
|
||||||
L1->nCcalls = 0;
|
|
||||||
L1->hookmask = L->hookmask;
|
L1->hookmask = L->hookmask;
|
||||||
L1->basehookcount = L->basehookcount;
|
L1->basehookcount = L->basehookcount;
|
||||||
L1->hook = L->hook;
|
L1->hook = L->hook;
|
||||||
@ -363,7 +364,6 @@ 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;
|
||||||
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;
|
||||||
|
1
lstate.h
1
lstate.h
@ -308,6 +308,7 @@ struct lua_State {
|
|||||||
int basehookcount;
|
int basehookcount;
|
||||||
int hookcount;
|
int hookcount;
|
||||||
volatile l_signalT hookmask;
|
volatile l_signalT hookmask;
|
||||||
|
StkId ptbc; /* pending to-be-closed variable */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -4358,10 +4358,6 @@ nor modified before a corresponding call to @Lid{lua_closeslot}.
|
|||||||
This function should not be called for an index
|
This function should not be called for an index
|
||||||
that is equal to or below an active to-be-closed index.
|
that is equal to or below an active to-be-closed index.
|
||||||
|
|
||||||
In the case of an out-of-memory error,
|
|
||||||
the value in the given index is immediately closed,
|
|
||||||
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,
|
||||||
|
@ -539,15 +539,17 @@ if rawget(_G, "T") then
|
|||||||
local _, msg = pcall(foo)
|
local _, msg = pcall(foo)
|
||||||
assert(msg == "not enough memory")
|
assert(msg == "not enough memory")
|
||||||
|
|
||||||
|
local closemsg
|
||||||
local close = func2close(function (self, msg)
|
local close = func2close(function (self, msg)
|
||||||
T.alloccount()
|
T.alloccount()
|
||||||
assert(msg == "not enough memory")
|
closemsg = msg
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- set a memory limit and return a closing object to remove the limit
|
-- set a memory limit and return a closing object to remove the limit
|
||||||
local function enter (count)
|
local function enter (count)
|
||||||
stack(10) -- reserve some stack space
|
stack(10) -- reserve some stack space
|
||||||
T.alloccount(count)
|
T.alloccount(count)
|
||||||
|
closemsg = nil
|
||||||
return close
|
return close
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -558,12 +560,7 @@ if rawget(_G, "T") then
|
|||||||
end
|
end
|
||||||
|
|
||||||
local _, msg = pcall(test)
|
local _, msg = pcall(test)
|
||||||
assert(msg == "not enough memory")
|
assert(msg == "not enough memory" and closemsg == "not enough memory")
|
||||||
|
|
||||||
-- now use metamethod for closing
|
|
||||||
close = setmetatable({}, {__close = function ()
|
|
||||||
T.alloccount()
|
|
||||||
end})
|
|
||||||
|
|
||||||
-- repeat test with extra closing upvalues
|
-- repeat test with extra closing upvalues
|
||||||
local function test ()
|
local function test ()
|
||||||
@ -580,7 +577,7 @@ if rawget(_G, "T") then
|
|||||||
end
|
end
|
||||||
|
|
||||||
local _, msg = pcall(test)
|
local _, msg = pcall(test)
|
||||||
assert(msg == 1000)
|
assert(msg == 1000 and closemsg == "not enough memory")
|
||||||
|
|
||||||
do -- testing 'toclose' in C string buffer
|
do -- testing 'toclose' in C string buffer
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user