From c90176f96924ee7d207501b32f216925773d3bdb Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 22 Oct 2018 14:55:51 -0300 Subject: [PATCH] Complete implementation of to-be-closed variables --- ldo.c | 9 ++++++--- testes/db.lua | 9 ++++----- testes/locals.lua | 33 +++++++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/ldo.c b/ldo.c index f2d12f04..78e4c5c3 100644 --- a/ldo.c +++ b/ldo.c @@ -676,12 +676,15 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, /* unroll continuation */ 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' */ - 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; } - else lua_assert(status == L->status); /* normal end or yield */ } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield : cast_int(L->top - (L->ci->func + 1)); diff --git a/testes/db.lua b/testes/db.lua index 2feaaef1..9da68210 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -734,19 +734,18 @@ a, b = coroutine.resume(co, 100) 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) a, b = coroutine.resume(co, 3) t = {"'coroutine.yield'", "'f'", "in function <"} -while coroutine.status(co) == "suspended" do +repeat checktraceback(co, t) a, b = coroutine.resume(co) table.insert(t, 2, "'f'") -- one more recursive call to 'f' -end -t[1] = "'error'" +until b checktraceback(co, t) diff --git a/testes/locals.lua b/testes/locals.lua index d12c70a0..f21fa2ec 100644 --- a/testes/locals.lua +++ b/testes/locals.lua @@ -274,13 +274,38 @@ if rawget(_G, "T") then 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 -local co = coroutine.wrap(function() +local co +co = coroutine.wrap(function() local scoped x = function () os.exit(false) end -- should not run - coroutine.yield() + co = nil + coroutine.yield() end) -co() -co = nil +co() -- start coroutine +assert(co == nil) -- eventually it will be collected print('OK')