From 409256b7849ec5ab3296cb0ab9eba3d65955d5ea Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 18 Dec 2020 11:22:42 -0300 Subject: '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. --- lstate.c | 8 +++++--- manual/manual.of | 10 +++++++--- testes/coroutine.lua | 10 +++++++++- testes/cstack.lua | 4 ++++ testes/locals.lua | 23 +++++++++++++++-------- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/lstate.c b/lstate.c index 1596b51c..96187c66 100644 --- a/lstate.c +++ b/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; diff --git a/manual/manual.of b/manual/manual.of index 771bace0..164e359a 100644 --- a/manual/manual.of +++ b/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. diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 5b927151..aaf565fb 100644 --- a/testes/coroutine.lua +++ b/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 = func2close(function (self, err) assert(err == nil); X = false diff --git a/testes/cstack.lua b/testes/cstack.lua index 8ac48e89..7bd55063 100644 --- a/testes/cstack.lua +++ b/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 diff --git a/testes/locals.lua b/testes/locals.lua index df44b86f..e2f6f35c 100644 --- a/testes/locals.lua +++ b/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 = func2close(function () x = x + 1; error("@YYY") end) + local xx = func2close(function () + x = x + 1; + checkwarn("@XXX"); error("@YYY") + end) local xv = 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 = func2close(function () y = y + 1; error("YYY") end) - local xv = func2close(function () x = x + 1; error("XXX") end) - coroutine.yield(100) - return 200 + local xx = func2close(function () + y = y + 1; checkwarn("XXX"); error("YYY") + end) + local xv = 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) -- cgit v1.2.3-55-g6feb