Complete implementation of to-be-closed variables

This commit is contained in:
Roberto Ierusalimschy 2018-10-22 14:55:51 -03:00
parent 3c7dc52909
commit c90176f969
3 changed files with 39 additions and 12 deletions

9
ldo.c
View File

@ -676,12 +676,15 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
/* unroll continuation */ /* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status); status = luaD_rawrunprotected(L, unroll, &status);
} }
if (unlikely(errorstatus(status))) { /* unrecoverable error? */ if (likely(!errorstatus(status)))
lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */
status = luaF_close(L, L->stack, status); /* close all upvalues */
L->status = cast_byte(status); /* mark thread as 'dead' */ L->status = cast_byte(status); /* mark thread as 'dead' */
luaD_seterrorobj(L, status, L->top); /* push error message */ luaD_seterrorobj(L, status, L->stack + 1); /* push error message */
L->ci = &L->base_ci; /* back to the original C level */
L->ci->top = L->top; L->ci->top = L->top;
} }
else lua_assert(status == L->status); /* normal end or yield */
} }
*nresults = (status == LUA_YIELD) ? L->ci->u2.nyield *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
: cast_int(L->top - (L->ci->func + 1)); : cast_int(L->top - (L->ci->func + 1));

View File

@ -734,19 +734,18 @@ a, b = coroutine.resume(co, 100)
assert(a and b == 30) assert(a and b == 30)
-- check traceback of suspended (or dead with error) coroutines -- check traceback of suspended coroutines
function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end function f(i) coroutine.yield(i == 0); f(i - 1) end
co = coroutine.create(function (x) f(x) end) co = coroutine.create(function (x) f(x) end)
a, b = coroutine.resume(co, 3) a, b = coroutine.resume(co, 3)
t = {"'coroutine.yield'", "'f'", "in function <"} t = {"'coroutine.yield'", "'f'", "in function <"}
while coroutine.status(co) == "suspended" do repeat
checktraceback(co, t) checktraceback(co, t)
a, b = coroutine.resume(co) a, b = coroutine.resume(co)
table.insert(t, 2, "'f'") -- one more recursive call to 'f' table.insert(t, 2, "'f'") -- one more recursive call to 'f'
end until b
t[1] = "'error'"
checktraceback(co, t) checktraceback(co, t)

View File

@ -274,13 +274,38 @@ if rawget(_G, "T") then
end end
-- to-be-closed variables in coroutines
do
-- an error in a coroutine closes variables
local x = false
local y = false
local co = coroutine.create(function ()
local scoped xv = function () x = true end
do
local scoped yv = function () y = true end
coroutine.yield(100) -- yield doesn't close variable
end
coroutine.yield(200) -- yield doesn't close variable
error(23) -- error does
end)
local a, b = coroutine.resume(co)
assert(a and b == 100 and not x and not y)
a, b = coroutine.resume(co)
assert(a and b == 200 and not x and y)
a, b = coroutine.resume(co)
assert(not a and b == 23 and x and y)
end
-- a suspended coroutine should not close its variables when collected -- a suspended coroutine should not close its variables when collected
local co = coroutine.wrap(function() local co
co = coroutine.wrap(function()
local scoped x = function () os.exit(false) end -- should not run local scoped x = function () os.exit(false) end -- should not run
co = nil
coroutine.yield() coroutine.yield()
end) end)
co() co() -- start coroutine
co = nil assert(co == nil) -- eventually it will be collected
print('OK') print('OK')