'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.
This commit is contained in:
Roberto Ierusalimschy 2020-12-18 11:22:42 -03:00
parent b17178b27a
commit 409256b784
5 changed files with 40 additions and 15 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)