summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-19 10:03:13 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-19 10:03:13 -0300
commit6ccd24eff58340c00db2877c4558a63c6b859442 (patch)
tree4c8f51657d8d1f4921846a23ba4b475db49fd837
parentd0f34d91373fa265d4445e456e4a10ce206c1559 (diff)
downloadlua-github-6ccd24eff58340c00db2877c4558a63c6b859442.tar.gz
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.
-rw-r--r--ldo.c1
-rw-r--r--lfunc.c37
-rw-r--r--lstate.c4
-rw-r--r--lstate.h1
-rw-r--r--manual/manual.of4
-rw-r--r--testes/locals.lua13
6 files changed, 25 insertions, 35 deletions
diff --git a/ldo.c b/ldo.c
index 45cfd592..9e3d6955 100644
--- a/ldo.c
+++ b/ldo.c
@@ -163,6 +163,7 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
if (oldstack == newstack)
return; /* stack address did not change */
L->top = (L->top - oldstack) + newstack;
+ lua_assert(L->ptbc == NULL);
for (up = L->openupval; up != NULL; up = up->u.open.next)
up->v = s2v((uplevel(up) - oldstack) + newstack);
for (ci = L->ci; ci != NULL; ci = ci->previous) {
diff --git a/lfunc.c b/lfunc.c
index 13e44d46..81ac9f0a 100644
--- a/lfunc.c
+++ b/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
-** (can raise a memory-allocation error)
-*/
-static void trynewtbcupval (lua_State *L, void *ud) {
- 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.
+** Create a to-be-closed upvalue. If there is 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
+** stack reallocation.)
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
TValue *obj = s2v(level);
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
- int status;
checkclosemth(L, level, obj);
- status = luaD_rawrunprotected(L, trynewtbcupval, level);
- if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
- lua_assert(status == LUA_ERRMEM);
- 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 */
- }
+ L->ptbc = level; /* in case of allocation error */
+ newupval(L, 1, level, &L->openupval);
+ L->ptbc = NULL; /* no errors */
}
}
@@ -196,11 +183,19 @@ void luaF_unlinkupval (UpVal *uv) {
/*
** Close all upvalues up to the given stack level. A 'status' equal
** 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) {
UpVal *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) {
TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top);
diff --git a/lstate.c b/lstate.c
index 92ccbf9b..c708a162 100644
--- a/lstate.c
+++ b/lstate.c
@@ -253,6 +253,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->ci = NULL;
L->nci = 0;
L->twups = L; /* thread has no upvalues */
+ L->nCcalls = 0;
L->errorJmp = NULL;
L->hook = NULL;
L->hookmask = 0;
@@ -263,6 +264,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->status = LUA_OK;
L->errfunc = 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);
api_incr_top(L);
preinit_thread(L1, g);
- L1->nCcalls = 0;
L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount;
L1->hook = L->hook;
@@ -363,7 +364,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
preinit_thread(L, g);
g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL;
- L->nCcalls = 0;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f;
g->ud = ud;
diff --git a/lstate.h b/lstate.h
index 38248e57..65d45264 100644
--- a/lstate.h
+++ b/lstate.h
@@ -308,6 +308,7 @@ struct lua_State {
int basehookcount;
int hookcount;
volatile l_signalT hookmask;
+ StkId ptbc; /* pending to-be-closed variable */
};
diff --git a/manual/manual.of b/manual/manual.of
index 2fe332a4..89069281 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -4358,10 +4358,6 @@ nor modified before a corresponding call to @Lid{lua_closeslot}.
This function should not be called for an 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,
by the time the @idx{__close} metamethod runs,
the @N{C stack} was already unwound,
diff --git a/testes/locals.lua b/testes/locals.lua
index 8506195e..24a95d18 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -539,15 +539,17 @@ if rawget(_G, "T") then
local _, msg = pcall(foo)
assert(msg == "not enough memory")
+ local closemsg
local close = func2close(function (self, msg)
T.alloccount()
- assert(msg == "not enough memory")
+ closemsg = msg
end)
-- set a memory limit and return a closing object to remove the limit
local function enter (count)
stack(10) -- reserve some stack space
T.alloccount(count)
+ closemsg = nil
return close
end
@@ -558,12 +560,7 @@ if rawget(_G, "T") then
end
local _, msg = pcall(test)
- assert(msg == "not enough memory")
-
- -- now use metamethod for closing
- close = setmetatable({}, {__close = function ()
- T.alloccount()
- end})
+ assert(msg == "not enough memory" and closemsg == "not enough memory")
-- repeat test with extra closing upvalues
local function test ()
@@ -580,7 +577,7 @@ if rawget(_G, "T") then
end
local _, msg = pcall(test)
- assert(msg == 1000)
+ assert(msg == 1000 and closemsg == "not enough memory")
do -- testing 'toclose' in C string buffer
collectgarbage()