Răsfoiți Sursa

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.
Roberto Ierusalimschy 4 ani în urmă
părinte
comite
6ccd24eff5
6 a modificat fișierele cu 25 adăugiri și 35 ștergeri
  1. 1 0
      ldo.c
  2. 16 21
      lfunc.c
  3. 2 2
      lstate.c
  4. 1 0
      lstate.h
  5. 0 4
      manual/manual.of
  6. 5 8
      testes/locals.lua

+ 1 - 0
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) {

+ 16 - 21
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);

+ 2 - 2
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;

+ 1 - 0
lstate.h

@@ -308,6 +308,7 @@ struct lua_State {
   int basehookcount;
   int hookcount;
   volatile l_signalT hookmask;
+  StkId ptbc;  /* pending to-be-closed variable */
 };
 
 

+ 0 - 4
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,

+ 5 - 8
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()