aboutsummaryrefslogtreecommitdiff
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-6ccd24eff58340c00db2877c4558a63c6b859442.tar.gz
lua-6ccd24eff58340c00db2877c4558a63c6b859442.tar.bz2
lua-6ccd24eff58340c00db2877c4558a63c6b859442.zip
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) {
163 if (oldstack == newstack) 163 if (oldstack == newstack)
164 return; /* stack address did not change */ 164 return; /* stack address did not change */
165 L->top = (L->top - oldstack) + newstack; 165 L->top = (L->top - oldstack) + newstack;
166 lua_assert(L->ptbc == NULL);
166 for (up = L->openupval; up != NULL; up = up->u.open.next) 167 for (up = L->openupval; up != NULL; up = up->u.open.next)
167 up->v = s2v((uplevel(up) - oldstack) + newstack); 168 up->v = s2v((uplevel(up) - oldstack) + newstack);
168 for (ci = L->ci; ci != NULL; ci = ci->previous) { 169 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) {
155 155
156 156
157/* 157/*
158** Try to create a to-be-closed upvalue 158** Create a to-be-closed upvalue. If there is a memory allocation error,
159** (can raise a memory-allocation error) 159** 'ptbc' keeps the object so it can be closed as soon as possible.
160*/ 160** (Since memory errors have no handler, that will happen before any
161static void trynewtbcupval (lua_State *L, void *ud) { 161** stack reallocation.)
162 newupval(L, 1, cast(StkId, ud), &L->openupval);
163}
164
165
166/*
167** Create a to-be-closed upvalue. If there is a memory error
168** when creating the upvalue, the closing method must be called here,
169** as there is no upvalue to call it later.
170*/ 162*/
171void luaF_newtbcupval (lua_State *L, StkId level) { 163void luaF_newtbcupval (lua_State *L, StkId level) {
172 TValue *obj = s2v(level); 164 TValue *obj = s2v(level);
173 lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); 165 lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
174 if (!l_isfalse(obj)) { /* false doesn't need to be closed */ 166 if (!l_isfalse(obj)) { /* false doesn't need to be closed */
175 int status;
176 checkclosemth(L, level, obj); 167 checkclosemth(L, level, obj);
177 status = luaD_rawrunprotected(L, trynewtbcupval, level); 168 L->ptbc = level; /* in case of allocation error */
178 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ 169 newupval(L, 1, level, &L->openupval);
179 lua_assert(status == LUA_ERRMEM); 170 L->ptbc = NULL; /* no errors */
180 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
181 callclosemethod(L, s2v(level), s2v(level + 1), 0);
182 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
183 }
184 } 171 }
185} 172}
186 173
@@ -196,11 +183,19 @@ void luaF_unlinkupval (UpVal *uv) {
196/* 183/*
197** Close all upvalues up to the given stack level. A 'status' equal 184** Close all upvalues up to the given stack level. A 'status' equal
198** to NOCLOSINGMETH closes upvalues without running any __close 185** to NOCLOSINGMETH closes upvalues without running any __close
199** metamethods. 186** metamethods. If there is a pending to-be-closed value, close
187** it before anything else.
200*/ 188*/
201void luaF_close (lua_State *L, StkId level, int status, int yy) { 189void luaF_close (lua_State *L, StkId level, int status, int yy) {
202 UpVal *uv; 190 UpVal *uv;
203 StkId upl; /* stack index pointed by 'uv' */ 191 StkId upl; /* stack index pointed by 'uv' */
192 if (unlikely(status == LUA_ERRMEM && L->ptbc != NULL)) {
193 upl = L->ptbc;
194 L->ptbc = NULL; /* remove from "list" before closing */
195 prepcallclosemth(L, upl, status, yy);
196 }
197 else
198 lua_assert(L->ptbc == NULL); /* must be empty for other status */
204 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { 199 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
205 TValue *slot = &uv->u.value; /* new position for value */ 200 TValue *slot = &uv->u.value; /* new position for value */
206 lua_assert(uplevel(uv) < L->top); 201 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) {
253 L->ci = NULL; 253 L->ci = NULL;
254 L->nci = 0; 254 L->nci = 0;
255 L->twups = L; /* thread has no upvalues */ 255 L->twups = L; /* thread has no upvalues */
256 L->nCcalls = 0;
256 L->errorJmp = NULL; 257 L->errorJmp = NULL;
257 L->hook = NULL; 258 L->hook = NULL;
258 L->hookmask = 0; 259 L->hookmask = 0;
@@ -263,6 +264,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
263 L->status = LUA_OK; 264 L->status = LUA_OK;
264 L->errfunc = 0; 265 L->errfunc = 0;
265 L->oldpc = 0; 266 L->oldpc = 0;
267 L->ptbc = NULL;
266} 268}
267 269
268 270
@@ -296,7 +298,6 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
296 setthvalue2s(L, L->top, L1); 298 setthvalue2s(L, L->top, L1);
297 api_incr_top(L); 299 api_incr_top(L);
298 preinit_thread(L1, g); 300 preinit_thread(L1, g);
299 L1->nCcalls = 0;
300 L1->hookmask = L->hookmask; 301 L1->hookmask = L->hookmask;
301 L1->basehookcount = L->basehookcount; 302 L1->basehookcount = L->basehookcount;
302 L1->hook = L->hook; 303 L1->hook = L->hook;
@@ -363,7 +364,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
363 preinit_thread(L, g); 364 preinit_thread(L, g);
364 g->allgc = obj2gco(L); /* by now, only object is the main thread */ 365 g->allgc = obj2gco(L); /* by now, only object is the main thread */
365 L->next = NULL; 366 L->next = NULL;
366 L->nCcalls = 0;
367 incnny(L); /* main thread is always non yieldable */ 367 incnny(L); /* main thread is always non yieldable */
368 g->frealloc = f; 368 g->frealloc = f;
369 g->ud = ud; 369 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 {
308 int basehookcount; 308 int basehookcount;
309 int hookcount; 309 int hookcount;
310 volatile l_signalT hookmask; 310 volatile l_signalT hookmask;
311 StkId ptbc; /* pending to-be-closed variable */
311}; 312};
312 313
313 314
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}.
4358This function should not be called for an index 4358This function should not be called for an index
4359that is equal to or below an active to-be-closed index. 4359that is equal to or below an active to-be-closed index.
4360 4360
4361In the case of an out-of-memory error,
4362the value in the given index is immediately closed,
4363as if it was already marked.
4364
4365Note that, both in case of errors and of a regular return, 4361Note that, both in case of errors and of a regular return,
4366by the time the @idx{__close} metamethod runs, 4362by the time the @idx{__close} metamethod runs,
4367the @N{C stack} was already unwound, 4363the @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
539 local _, msg = pcall(foo) 539 local _, msg = pcall(foo)
540 assert(msg == "not enough memory") 540 assert(msg == "not enough memory")
541 541
542 local closemsg
542 local close = func2close(function (self, msg) 543 local close = func2close(function (self, msg)
543 T.alloccount() 544 T.alloccount()
544 assert(msg == "not enough memory") 545 closemsg = msg
545 end) 546 end)
546 547
547 -- set a memory limit and return a closing object to remove the limit 548 -- set a memory limit and return a closing object to remove the limit
548 local function enter (count) 549 local function enter (count)
549 stack(10) -- reserve some stack space 550 stack(10) -- reserve some stack space
550 T.alloccount(count) 551 T.alloccount(count)
552 closemsg = nil
551 return close 553 return close
552 end 554 end
553 555
@@ -558,12 +560,7 @@ if rawget(_G, "T") then
558 end 560 end
559 561
560 local _, msg = pcall(test) 562 local _, msg = pcall(test)
561 assert(msg == "not enough memory") 563 assert(msg == "not enough memory" and closemsg == "not enough memory")
562
563 -- now use metamethod for closing
564 close = setmetatable({}, {__close = function ()
565 T.alloccount()
566 end})
567 564
568 -- repeat test with extra closing upvalues 565 -- repeat test with extra closing upvalues
569 local function test () 566 local function test ()
@@ -580,7 +577,7 @@ if rawget(_G, "T") then
580 end 577 end
581 578
582 local _, msg = pcall(test) 579 local _, msg = pcall(test)
583 assert(msg == 1000) 580 assert(msg == 1000 and closemsg == "not enough memory")
584 581
585 do -- testing 'toclose' in C string buffer 582 do -- testing 'toclose' in C string buffer
586 collectgarbage() 583 collectgarbage()