浏览代码

'coroutine.close'/'lua_resetthread' report original errors

Besides errors in closing methods, 'coroutine.close' and
'lua_resetthread' also consider the original error that stopped the
thread, if any.
Roberto Ierusalimschy 4 年之前
父节点
当前提交
409256b784
共有 5 个文件被更改,包括 40 次插入15 次删除
  1. 5 3
      lstate.c
  2. 7 3
      manual/manual.of
  3. 9 1
      testes/coroutine.lua
  4. 4 0
      testes/cstack.lua
  5. 15 8
      testes/locals.lua

+ 5 - 3
lstate.c

@@ -323,14 +323,16 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
 
 int lua_resetthread (lua_State *L) {
   CallInfo *ci;
-  int status;
+  int status = L->status;
   lua_lock(L);
   L->ci = ci = &L->base_ci;  /* unwind CallInfo list */
   setnilvalue(s2v(L->stack));  /* 'function' entry for basic 'ci' */
   ci->func = L->stack;
   ci->callstatus = CIST_C;
-  status = luaF_close(L, L->stack, CLOSEPROTECT);
-  if (status != CLOSEPROTECT)  /* real errors? */
+  if (status == LUA_OK || status == LUA_YIELD)
+    status = CLOSEPROTECT;  /* run closing methods in protected mode */
+  status = luaF_close(L, L->stack, status);
+  if (status != CLOSEPROTECT)  /* errors? */
     luaD_seterrorobj(L, status, L->stack + 1);
   else {
     status = LUA_OK;

+ 7 - 3
manual/manual.of

@@ -4098,10 +4098,12 @@ and then pops the top element.
 Resets a thread, cleaning its call stack and closing all pending
 to-be-closed variables.
 Returns a status code:
-@Lid{LUA_OK} for no errors in closing methods,
+@Lid{LUA_OK} for no errors in the thread
+(either the original error that stopped the thread or
+errors in closing methods),
 or an error status otherwise.
 In case of error,
-leaves the error object on the top of the stack,
+leaves the error object on the top of the stack.
 
 }
 
@@ -6577,7 +6579,9 @@ that is,
 closes all its pending to-be-closed variables
 and puts the coroutine in a dead state.
 The given coroutine must be dead or suspended.
-In case of error closing some variable,
+In case of error
+(either the original error that stopped the coroutine or
+errors in closing methods),
 returns @false plus the error object;
 otherwise returns @true.
 

+ 9 - 1
testes/coroutine.lua

@@ -134,7 +134,8 @@ do
   local co = coroutine.create(print)
   assert(coroutine.resume(co, "testing 'coroutine.close'"))
   assert(coroutine.status(co) == "dead")
-  assert(coroutine.close(co))
+  local st, msg = coroutine.close(co)
+  assert(st and msg == nil)
 
   -- cannot close the running coroutine
   local st, msg = pcall(coroutine.close, coroutine.running())
@@ -151,6 +152,13 @@ do
   -- to-be-closed variables in coroutines
   local X
 
+  -- closing a coroutine after an error
+  local co = coroutine.create(error)
+  local st, msg = coroutine.resume(co, 100)
+  assert(not st and msg == 100)
+  st, msg = coroutine.close(co)
+  assert(not st and msg == 100)
+
   co = coroutine.create(function ()
     local x <close> = func2close(function (self, err)
       assert(err == nil); X = false

+ 4 - 0
testes/cstack.lua

@@ -135,14 +135,18 @@ if T then
   local topB, sizeB   -- top and size Before overflow
   local topA, sizeA   -- top and size After overflow
   topB, sizeB = T.stacklevel()
+  collectgarbage("stop")    -- __gc should not be called with a full stack
   xpcall(f, err)
+  collectgarbage("restart")
   topA, sizeA = T.stacklevel()
   -- sizes should be comparable
   assert(topA == topB and sizeA < sizeB * 2)
   print(string.format("maximum stack size: %d", stack1))
   LIM = N      -- will stop recursion at maximum level
   N = 0        -- to count again
+  collectgarbage("stop")    -- __gc should not be called with a full stack
   f()
+  collectgarbage("restart")
   print"+"
 end
 

+ 15 - 8
testes/locals.lua

@@ -362,7 +362,7 @@ end
 
 local function checkwarn (msg)
   if T then
-    assert(string.find(_WARN, msg))
+    assert(_WARN and string.find(_WARN, msg))
     _WARN = false    -- reset variable to check next warning
   end
 end
@@ -670,10 +670,13 @@ do
   -- error in a wrapped coroutine raising errors when closing a variable
   local x = 0
   local co = coroutine.wrap(function ()
-    local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
+    local xx <close> = func2close(function ()
+      x = x + 1;
+      checkwarn("@XXX"); error("@YYY")
+    end)
     local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
-      coroutine.yield(100)
-      error(200)
+    coroutine.yield(100)
+    error(200)
   end)
   assert(co() == 100); assert(x == 0)
   local st, msg = pcall(co); assert(x == 2)
@@ -683,10 +686,14 @@ do
   local x = 0
   local y = 0
   co = coroutine.wrap(function ()
-    local xx <close> = func2close(function () y = y + 1; error("YYY") end)
-    local xv <close> = func2close(function () x = x + 1; error("XXX") end)
-      coroutine.yield(100)
-      return 200
+    local xx <close> = func2close(function ()
+      y = y + 1; checkwarn("XXX"); error("YYY")
+    end)
+    local xv <close> = func2close(function ()
+      x = x + 1; error("XXX")
+    end)
+    coroutine.yield(100)
+    return 200
   end)
   assert(co() == 100); assert(x == 0)
   local st, msg = pcall(co)