Browse Source

Bug: Wrong status in coroutine during reset

When closing variables during 'coroutine.close' or 'lua_resetthread',
the status of a coroutine must be set to LUA_OK; a coroutine should
not run with any other status. (See assertion in 'lua_callk'.)

After the reset, the status should be kept as normal, as any error
was already reported.
pull/27/head
Roberto Ierusalimschy 3 years ago
parent
commit
bfbff3703e
  1. 4
      lcorolib.c
  2. 4
      lstate.c
  3. 44
      testes/coroutine.lua

4
lcorolib.c

@ -78,7 +78,7 @@ static int luaB_auxwrap (lua_State *L) {
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
stat = lua_resetthread(co); /* close its tbc variables */ stat = lua_resetthread(co); /* close its tbc variables */
lua_assert(stat != LUA_OK); lua_assert(stat != LUA_OK);
lua_xmove(co, L, 1); /* copy error message */ lua_xmove(co, L, 1); /* move error message to the caller */
} }
if (stat != LUA_ERRMEM && /* not a memory error and ... */ if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
@ -179,7 +179,7 @@ static int luaB_close (lua_State *L) {
} }
else { else {
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
lua_xmove(co, L, 1); /* copy error message */ lua_xmove(co, L, 1); /* move error message */
return 2; return 2;
} }
} }

4
lstate.c

@ -166,7 +166,7 @@ void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS) if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow"); luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
} }
@ -330,13 +330,13 @@ int luaE_resetthread (lua_State *L, int status) {
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
if (status == LUA_YIELD) if (status == LUA_YIELD)
status = LUA_OK; status = LUA_OK;
L->status = LUA_OK; /* so it can run __close metamethods */
status = luaD_closeprotected(L, 1, status); status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */ if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack + 1); luaD_seterrorobj(L, status, L->stack + 1);
else else
L->top = L->stack + 1; L->top = L->stack + 1;
ci->top = L->top + LUA_MINSTACK; ci->top = L->top + LUA_MINSTACK;
L->status = cast_byte(status);
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
return status; return status;
} }

44
testes/coroutine.lua

@ -136,6 +136,10 @@ do
assert(coroutine.status(co) == "dead") assert(coroutine.status(co) == "dead")
local st, msg = coroutine.close(co) local st, msg = coroutine.close(co)
assert(st and msg == nil) assert(st and msg == nil)
-- also ok to close it again
st, msg = coroutine.close(co)
assert(st and msg == nil)
-- cannot close the running coroutine -- cannot close the running coroutine
local st, msg = pcall(coroutine.close, coroutine.running()) local st, msg = pcall(coroutine.close, coroutine.running())
@ -149,6 +153,22 @@ do
assert(not st and string.find(msg, "normal")) assert(not st and string.find(msg, "normal"))
end))() end))()
-- cannot close a coroutine while closing it
do
local co
co = coroutine.create(
function()
local x <close> = func2close(function()
coroutine.close(co) -- try to close it again
end)
coroutine.yield(20)
end)
local st, msg = coroutine.resume(co)
assert(st and msg == 20)
st, msg = coroutine.close(co)
assert(not st and string.find(msg, "running coroutine"))
end
-- to-be-closed variables in coroutines -- to-be-closed variables in coroutines
local X local X
@ -158,6 +178,9 @@ do
assert(not st and msg == 100) assert(not st and msg == 100)
st, msg = coroutine.close(co) st, msg = coroutine.close(co)
assert(not st and msg == 100) assert(not st and msg == 100)
-- after closing, no more errors
st, msg = coroutine.close(co)
assert(st and msg == nil)
co = coroutine.create(function () co = coroutine.create(function ()
local x <close> = func2close(function (self, err) local x <close> = func2close(function (self, err)
@ -189,6 +212,9 @@ do
local st, msg = coroutine.close(co) local st, msg = coroutine.close(co)
assert(st == false and coroutine.status(co) == "dead" and msg == 200) assert(st == false and coroutine.status(co) == "dead" and msg == 200)
assert(x == 200) assert(x == 200)
-- after closing, no more errors
st, msg = coroutine.close(co)
assert(st and msg == nil)
end end
do do
@ -419,7 +445,7 @@ do
local X = false local X = false
A = coroutine.wrap(function() A = coroutine.wrap(function()
local _ <close> = setmetatable({}, {__close = function () X = true end}) local _ <close> = func2close(function () X = true end)
return pcall(A, 1) return pcall(A, 1)
end) end)
st, res = A() st, res = A()
@ -427,6 +453,22 @@ do
end end
-- bug in 5.4.1
do
-- coroutine ran close metamethods with invalid status during a
-- reset.
local co
co = coroutine.wrap(function()
local x <close> = func2close(function() return pcall(co) end)
error(111)
end)
local st, errobj = pcall(co)
assert(not st and errobj == 111)
st, errobj = pcall(co)
assert(not st and string.find(errobj, "dead coroutine"))
end
-- attempt to resume 'normal' coroutine -- attempt to resume 'normal' coroutine
local co1, co2 local co1, co2
co1 = coroutine.create(function () return co2() end) co1 = coroutine.create(function () return co2() end)

Loading…
Cancel
Save