diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-19 10:03:13 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-19 10:03:13 -0300 |
commit | 6ccd24eff58340c00db2877c4558a63c6b859442 (patch) | |
tree | 4c8f51657d8d1f4921846a23ba4b475db49fd837 | |
parent | d0f34d91373fa265d4445e456e4a10ce206c1559 (diff) | |
download | lua-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.c | 1 | ||||
-rw-r--r-- | lfunc.c | 37 | ||||
-rw-r--r-- | lstate.c | 4 | ||||
-rw-r--r-- | lstate.h | 1 | ||||
-rw-r--r-- | manual/manual.of | 4 | ||||
-rw-r--r-- | testes/locals.lua | 13 |
6 files changed, 25 insertions, 35 deletions
@@ -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) { |
@@ -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 |
161 | static 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 | */ |
171 | void luaF_newtbcupval (lua_State *L, StkId level) { | 163 | void 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 | */ |
201 | void luaF_close (lua_State *L, StkId level, int status, int yy) { | 189 | void 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); |
@@ -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; |
@@ -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}. | |||
4358 | This function should not be called for an index | 4358 | This function should not be called for an index |
4359 | that is equal to or below an active to-be-closed index. | 4359 | that is equal to or below an active to-be-closed index. |
4360 | 4360 | ||
4361 | In the case of an out-of-memory error, | ||
4362 | the value in the given index is immediately closed, | ||
4363 | as if it was already marked. | ||
4364 | |||
4365 | Note that, both in case of errors and of a regular return, | 4361 | Note that, both in case of errors and of a regular return, |
4366 | by the time the @idx{__close} metamethod runs, | 4362 | by the time the @idx{__close} metamethod runs, |
4367 | the @N{C stack} was already unwound, | 4363 | 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 | |||
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() |