1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- -- $Id: testes/coroutine.lua $
- -- See Copyright Notice in file lua.h
- print "testing coroutines"
- local debug = require'debug'
- local f
- local main, ismain = coroutine.running()
- assert(type(main) == "thread" and ismain)
- assert(not coroutine.resume(main))
- assert(not coroutine.isyieldable(main) and not coroutine.isyieldable())
- assert(not pcall(coroutine.yield))
- -- trivial errors
- assert(not pcall(coroutine.resume, 0))
- assert(not pcall(coroutine.status, 0))
- -- tests for multiple yield/resume arguments
- local function eqtab (t1, t2)
- assert(#t1 == #t2)
- for i = 1, #t1 do
- local v = t1[i]
- assert(t2[i] == v)
- end
- end
- _G.x = nil -- declare x
- _G.f = nil -- declare f
- local function foo (a, ...)
- local x, y = coroutine.running()
- assert(x == f and y == false)
- -- next call should not corrupt coroutine (but must fail,
- -- as it attempts to resume the running coroutine)
- assert(coroutine.resume(f) == false)
- assert(coroutine.status(f) == "running")
- local arg = {...}
- assert(coroutine.isyieldable(x))
- for i=1,#arg do
- _G.x = {coroutine.yield(table.unpack(arg[i]))}
- end
- return table.unpack(a)
- end
- f = coroutine.create(foo)
- assert(coroutine.isyieldable(f))
- assert(type(f) == "thread" and coroutine.status(f) == "suspended")
- assert(string.find(tostring(f), "thread"))
- local s,a,b,c,d
- s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
- assert(coroutine.isyieldable(f))
- assert(s and a == nil and coroutine.status(f) == "suspended")
- s,a,b,c,d = coroutine.resume(f)
- eqtab(_G.x, {})
- assert(s and a == 1 and b == nil)
- assert(coroutine.isyieldable(f))
- s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
- eqtab(_G.x, {1, 2, 3})
- assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
- s,a,b,c,d = coroutine.resume(f, "xuxu")
- eqtab(_G.x, {"xuxu"})
- assert(s and a == 1 and b == 2 and c == 3 and d == nil)
- assert(coroutine.status(f) == "dead")
- s, a = coroutine.resume(f, "xuxu")
- assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
- _G.f = nil
- -- yields in tail calls
- local function foo (i) return coroutine.yield(i) end
- local f = coroutine.wrap(function ()
- for i=1,10 do
- assert(foo(i) == _G.x)
- end
- return 'a'
- end)
- for i=1,10 do _G.x = i; assert(f(i) == i) end
- _G.x = 'xuxu'; assert(f('xuxu') == 'a')
- _G.x = nil
- -- recursive
- local function pf (n, i)
- coroutine.yield(n)
- pf(n*i, i+1)
- end
- f = coroutine.wrap(pf)
- local s=1
- for i=1,10 do
- assert(f(1, 1) == s)
- s = s*i
- end
- -- sieve
- local function gen (n)
- return coroutine.wrap(function ()
- for i=2,n do coroutine.yield(i) end
- end)
- end
- local function filter (p, g)
- return coroutine.wrap(function ()
- while 1 do
- local n = g()
- if n == nil then return end
- if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
- end
- end)
- end
- local x = gen(80)
- local a = {}
- while 1 do
- local n = x()
- if n == nil then break end
- table.insert(a, n)
- x = filter(n, x)
- end
- assert(#a == 22 and a[#a] == 79)
- x, a = nil
- do -- "bug" in 5.4.2
- local function foo () foo () end -- just create a stack overflow
- local co = coroutine.create(foo)
- -- running this coroutine would overflow the unsigned short 'nci', the
- -- counter of CallInfo structures available to the thread.
- -- (The issue only manifests in an 'assert'.)
- local st, msg = coroutine.resume(co)
- assert(string.find(msg, "stack overflow"))
- assert(coroutine.status(co) == "dead")
- end
- print("to-be-closed variables in coroutines")
- local function func2close (f)
- return setmetatable({}, {__close = f})
- end
- do
- -- ok to close a dead coroutine
- local co = coroutine.create(print)
- assert(coroutine.resume(co, "testing 'coroutine.close'"))
- assert(coroutine.status(co) == "dead")
- local st, msg = coroutine.close(co)
- assert(st and msg == nil)
- -- also ok to close it again
- st, msg = coroutine.close(co)
- assert(st and msg == nil)
- local main = coroutine.running()
- -- cannot close 'main'
- local st, msg = pcall(coroutine.close, main);
- assert(not st and string.find(msg, "main"))
- -- cannot close a "normal" coroutine
- ;(coroutine.wrap(function ()
- local st, msg = pcall(coroutine.close, main)
- assert(not st and string.find(msg, "normal"))
- end))()
- do -- close a coroutine while closing it
- local co
- co = coroutine.create(
- function()
- local x <close> = func2close(function()
- coroutine.close(co) -- 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(st and msg == nil)
- end
- -- 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)
- -- after closing, no more errors
- st, msg = coroutine.close(co)
- assert(st and msg == nil)
- co = coroutine.create(function ()
- local x <close> = func2close(function (self, err)
- assert(err == nil); X = false
- end)
- X = true
- coroutine.yield()
- end)
- coroutine.resume(co)
- assert(X)
- assert(coroutine.close(co))
- assert(not X and coroutine.status(co) == "dead")
- -- error closing a coroutine
- local x = 0
- co = coroutine.create(function()
- local y <close> = func2close(function (self,err)
- assert(err == 111)
- x = 200
- error(200)
- end)
- local x <close> = func2close(function (self, err)
- assert(err == nil); error(111)
- end)
- coroutine.yield()
- end)
- coroutine.resume(co)
- assert(x == 0)
- local st, msg = coroutine.close(co)
- assert(st == false and coroutine.status(co) == "dead" and msg == 200)
- assert(x == 200)
- -- after closing, no more errors
- st, msg = coroutine.close(co)
- assert(st and msg == nil)
- end
- do
- -- <close> versus pcall in coroutines
- local X = false
- local Y = false
- local function foo ()
- local x <close> = func2close(function (self, err)
- Y = debug.getinfo(2)
- X = err
- end)
- error(43)
- end
- local co = coroutine.create(function () return pcall(foo) end)
- local st1, st2, err = coroutine.resume(co)
- assert(st1 and not st2 and err == 43)
- assert(X == 43 and Y.what == "C")
- -- recovering from errors in __close metamethods
- local track = {}
- local function h (o)
- local hv <close> = o
- return 1
- end
- local function foo ()
- local x <close> = func2close(function(_,msg)
- track[#track + 1] = msg or false
- error(20)
- end)
- local y <close> = func2close(function(_,msg)
- track[#track + 1] = msg or false
- return 1000
- end)
- local z <close> = func2close(function(_,msg)
- track[#track + 1] = msg or false
- error(10)
- end)
- coroutine.yield(1)
- h(func2close(function(_,msg)
- track[#track + 1] = msg or false
- error(2)
- end))
- end
- local co = coroutine.create(pcall)
- local st, res = coroutine.resume(co, foo) -- call 'foo' protected
- assert(st and res == 1) -- yield 1
- local st, res1, res2 = coroutine.resume(co) -- continue
- assert(coroutine.status(co) == "dead")
- assert(st and not res1 and res2 == 20) -- last error (20)
- assert(track[1] == false and track[2] == 2 and track[3] == 10 and
- track[4] == 10)
- end
- do print("coroutines closing itself")
- global <const> coroutine, string, os
- global <const> assert, error, pcall
- local X = nil
- local function new ()
- return coroutine.create(function (what)
- local <close>var = func2close(function (t, err)
- if what == "yield" then
- coroutine.yield()
- elseif what == "error" then
- error(200)
- else
- X = "Ok"
- return X
- end
- end)
- -- do an unprotected call so that coroutine becomes non-yieldable
- string.gsub("a", "a", function ()
- assert(not coroutine.isyieldable())
- -- do protected calls while non-yieldable, to add recovery
- -- entries (setjmp) to the stack
- assert(pcall(pcall, function ()
- -- 'close' works even while non-yieldable
- coroutine.close() -- close itself
- os.exit(false) -- not reacheable
- end))
- end)
- end)
- end
- local co = new()
- local st, msg = coroutine.resume(co, "ret")
- assert(st and msg == nil)
- assert(X == "Ok")
- local co = new()
- local st, msg = coroutine.resume(co, "error")
- assert(not st and msg == 200)
- local co = new()
- local st, msg = coroutine.resume(co, "yield")
- assert(not st and string.find(msg, "attempt to yield"))
- end
- -- yielding across C boundaries
- local co = coroutine.wrap(function()
- assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
- assert(coroutine.isyieldable())
- coroutine.yield(20)
- return 30
- end)
- assert(co() == 20)
- assert(co() == 30)
- local f = function (s, i) return coroutine.yield(i) end
- local f1 = coroutine.wrap(function ()
- return xpcall(pcall, function (...) return ... end,
- function ()
- local s = 0
- for i in f, nil, 1 do pcall(function () s = s + i end) end
- error({s})
- end)
- end)
- f1()
- for i = 1, 10 do assert(f1(i) == i) end
- local r1, r2, v = f1(nil)
- assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
- local function f (a, b) a = coroutine.yield(a); error{a + b} end
- local function g(x) return x[1]*2 end
- co = coroutine.wrap(function ()
- coroutine.yield(xpcall(f, g, 10, 20))
- end)
- assert(co() == 10)
- local r, msg = co(100)
- assert(not r and msg == 240)
- -- unyieldable C call
- do
- local function f (c)
- assert(not coroutine.isyieldable())
- return c .. c
- end
- local co = coroutine.wrap(function (c)
- assert(coroutine.isyieldable())
- local s = string.gsub("a", ".", f)
- return s
- end)
- assert(co() == "aa")
- end
- do -- testing single trace of coroutines
- local X
- local co = coroutine.create(function ()
- coroutine.yield(10)
- return 20;
- end)
- local trace = {}
- local function dotrace (event)
- trace[#trace + 1] = event
- end
- debug.sethook(co, dotrace, "clr")
- repeat until not coroutine.resume(co)
- local correcttrace = {"call", "line", "call", "return", "line", "return"}
- assert(#trace == #correcttrace)
- for k, v in pairs(trace) do
- assert(v == correcttrace[k])
- end
- end
- -- errors in coroutines
- function foo ()
- assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
- assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
- coroutine.yield(3)
- error(foo)
- end
- function goo() foo() end
- x = coroutine.wrap(goo)
- assert(x() == 3)
- local a,b = pcall(x)
- assert(not a and b == foo)
- x = coroutine.create(goo)
- a,b = coroutine.resume(x)
- assert(a and b == 3)
- a,b = coroutine.resume(x)
- assert(not a and b == foo and coroutine.status(x) == "dead")
- a,b = coroutine.resume(x)
- assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
- goo = nil
- -- co-routines x for loop
- local function all (a, n, k)
- if k == 0 then coroutine.yield(a)
- else
- for i=1,n do
- a[k] = i
- all(a, n, k-1)
- end
- end
- end
- local a = 0
- for t in coroutine.wrap(function () all({}, 5, 4) end) do
- a = a+1
- end
- assert(a == 5^4)
- -- access to locals of collected corroutines
- local C = {}; setmetatable(C, {__mode = "kv"})
- local x = coroutine.wrap (function ()
- local a = 10
- local function f () a = a+10; return a end
- while true do
- a = a+1
- coroutine.yield(f)
- end
- end)
- C[1] = x;
- local f = x()
- assert(f() == 21 and x()() == 32 and x() == f)
- x = nil
- collectgarbage()
- assert(C[1] == undef)
- assert(f() == 43 and f() == 53)
- -- old bug: attempt to resume itself
- local function co_func (current_co)
- assert(coroutine.running() == current_co)
- assert(coroutine.resume(current_co) == false)
- coroutine.yield(10, 20)
- assert(coroutine.resume(current_co) == false)
- coroutine.yield(23)
- return 10
- end
- local co = coroutine.create(co_func)
- local a,b,c = coroutine.resume(co, co)
- assert(a == true and b == 10 and c == 20)
- a,b = coroutine.resume(co, co)
- assert(a == true and b == 23)
- a,b = coroutine.resume(co, co)
- assert(a == true and b == 10)
- assert(coroutine.resume(co, co) == false)
- assert(coroutine.resume(co, co) == false)
- -- other old bug when attempting to resume itself
- -- (trigger C-code assertions)
- do
- local A = coroutine.running()
- local B = coroutine.create(function() return coroutine.resume(A) end)
- local st, res = coroutine.resume(B)
- assert(st == true and res == false)
- local X = false
- A = coroutine.wrap(function()
- local _ <close> = func2close(function () X = true end)
- return pcall(A, 1)
- end)
- st, res = A()
- assert(not st and string.find(res, "non%-suspended") and X == true)
- 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
- local co1, co2
- co1 = coroutine.create(function () return co2() end)
- co2 = coroutine.wrap(function ()
- assert(coroutine.status(co1) == 'normal')
- assert(not coroutine.resume(co1))
- coroutine.yield(3)
- end)
- a,b = coroutine.resume(co1)
- assert(a and b == 3)
- assert(coroutine.status(co1) == 'dead')
- -- infinite recursion of coroutines
- a = function(a) coroutine.wrap(a)(a) end
- assert(not pcall(a, a))
- a = nil
- do
- -- bug in 5.4: thread can use message handler higher in the stack
- -- than the variable being closed
- local c = coroutine.create(function()
- local clo <close> = setmetatable({}, {__close=function()
- local x = 134 -- will overwrite message handler
- error(x)
- end})
- -- yields coroutine but leaves a new message handler for it,
- -- that would be used when closing the coroutine (except that it
- -- will be overwritten)
- xpcall(coroutine.yield, function() return "XXX" end)
- end)
- assert(coroutine.resume(c)) -- start coroutine
- local st, msg = coroutine.close(c)
- assert(not st and msg == 134)
- end
- -- access to locals of erroneous coroutines
- local x = coroutine.create (function ()
- local a = 10
- _G.F = function () a=a+1; return a end
- error('x')
- end)
- assert(not coroutine.resume(x))
- -- overwrite previous position of local `a'
- assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
- assert(_G.F() == 11)
- assert(_G.F() == 12)
- _G.F = nil
- if not T then
- (Message or print)
- ('\n >>> testC not active: skipping coroutine API tests <<<\n')
- else
- print "testing yields inside hooks"
- local turn
- local function fact (t, x)
- assert(turn == t)
- if x == 0 then return 1
- else return x*fact(t, x-1)
- end
- end
- local A, B = 0, 0
- local x = coroutine.create(function ()
- T.sethook("yield 0", "", 2)
- A = fact("A", 6)
- end)
- local y = coroutine.create(function ()
- T.sethook("yield 0", "", 3)
- B = fact("B", 7)
- end)
- while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
- if A==0 then turn = "A"; assert(T.resume(x)) end
- if B==0 then turn = "B"; assert(T.resume(y)) end
- -- check that traceback works correctly after yields inside hooks
- debug.traceback(x)
- debug.traceback(y)
- end
- assert(B // A == 7) -- fact(7) // fact(6)
- do -- hooks vs. multiple values
- local done
- local function test (n)
- done = false
- return coroutine.wrap(function ()
- local a = {}
- for i = 1, n do a[i] = i end
- -- 'pushint' just to perturb the stack
- T.sethook("pushint 10; yield 0", "", 1) -- yield at each op.
- local a1 = {table.unpack(a)} -- must keep top between ops.
- assert(#a1 == n)
- for i = 1, n do assert(a[i] == i) end
- done = true
- end)
- end
- -- arguments to the coroutine are just to perturb its stack
- local co = test(0); while not done do co(30) end
- co = test(1); while not done do co(20, 10) end
- co = test(3); while not done do co() end
- co = test(100); while not done do co() end
- end
- local line = debug.getinfo(1, "l").currentline + 2 -- get line number
- local function foo ()
- local x = 10 --<< this line is 'line'
- x = x + 10
- _G.XX = x
- end
- -- testing yields in line hook
- local co = coroutine.wrap(function ()
- T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
- _G.XX = nil;
- _G.X = nil; co(); assert(_G.X == line)
- _G.X = nil; co(); assert(_G.X == line + 1)
- _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
- _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
- assert(co() == 10)
- _G.X = nil
- -- testing yields in count hook
- co = coroutine.wrap(function ()
- T.sethook("yield 0", "", 1); foo(); return 10 end)
- _G.XX = nil;
- local c = 0
- repeat c = c + 1; local a = co() until a == 10
- assert(_G.XX == 20 and c >= 5)
- co = coroutine.wrap(function ()
- T.sethook("yield 0", "", 2); foo(); return 10 end)
- _G.XX = nil;
- local c = 0
- repeat c = c + 1; local a = co() until a == 10
- assert(_G.XX == 20 and c >= 5)
- _G.X = nil; _G.XX = nil
- do
- -- testing debug library on a coroutine suspended inside a hook
- -- (bug in 5.2/5.3)
- c = coroutine.create(function (a, ...)
- T.sethook("yield 0", "l") -- will yield on next two lines
- local b = a
- return ...
- end)
- assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
- local n,v = debug.getlocal(c, 0, 1) -- check its local
- assert(n == "a" and v == 1 and debug.getlocal(c, 0, 2) ~= "b")
- assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
- local t = debug.getinfo(c, 0) -- test 'getinfo'
- assert(t.currentline == t.linedefined + 2)
- assert(not debug.getinfo(c, 1)) -- no other level
- assert(coroutine.resume(c)) -- run next line
- local n,v = debug.getlocal(c, 0, 2) -- check next local
- assert(n == "b" and v == 10)
- v = {coroutine.resume(c)} -- finish coroutine
- assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)
- assert(not coroutine.resume(c))
- end
- do
- -- testing debug library on last function in a suspended coroutine
- -- (bug in 5.2/5.3)
- local c = coroutine.create(function () T.testC("yield 1", 10, 20) end)
- local a, b = coroutine.resume(c)
- assert(a and b == 20)
- assert(debug.getinfo(c, 0).linedefined == -1)
- a, b = debug.getlocal(c, 0, 2)
- assert(b == 10)
- end
- print "testing coroutine API"
- -- reusing a thread
- assert(T.testC([[
- newthread # create thread
- pushvalue 2 # push body
- pushstring 'a a a' # push argument
- xmove 0 3 2 # move values to new thread
- resume -1, 1 # call it first time
- pushstatus
- xmove 3 0 0 # move results back to stack
- setglobal X # result
- setglobal Y # status
- pushvalue 2 # push body (to call it again)
- pushstring 'b b b'
- xmove 0 3 2
- resume -1, 1 # call it again
- pushstatus
- xmove 3 0 0
- return 1 # return result
- ]], function (...) return ... end) == 'b b b')
- assert(X == 'a a a' and Y == 'OK')
- X, Y = nil
- -- resuming running coroutine
- C = coroutine.create(function ()
- return T.testC([[
- pushnum 10;
- pushnum 20;
- resume -3 2;
- pushstatus
- gettop;
- return 3]], C)
- end)
- local a, b, c, d = coroutine.resume(C)
- assert(a == true and string.find(b, "non%-suspended") and
- c == "ERRRUN" and d == 4)
- a, b, c, d = T.testC([[
- rawgeti R !M # get main thread
- pushnum 10;
- pushnum 20;
- resume -3 2;
- pushstatus
- gettop;
- return 4]])
- assert(a == coroutine.running() and string.find(b, "non%-suspended") and
- c == "ERRRUN" and d == 4)
- -- using a main thread as a coroutine (dubious use!)
- local state = T.newstate()
- -- check that yielddable is working correctly
- assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
- -- main thread is not yieldable
- assert(not T.testC(state, "rawgeti R !M; isyieldable -1; remove 1; return 1"))
- T.testC(state, "settop 0")
- T.loadlib(state, 1 | 2, 4) -- load _G and 'package', preload 'coroutine'
- assert(T.doremote(state, [[
- coroutine = require'coroutine';
- X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
- return 'ok']]))
- local t = table.pack(T.testC(state, [[
- rawgeti R !M # get main thread
- pushstring 'XX'
- getglobal X # get function for body
- pushstring AA # arg
- resume 1 1 # 'resume' shadows previous stack!
- gettop
- setglobal T # top
- setglobal B # second yielded value
- setglobal A # fist yielded value
- rawgeti R !M # get main thread
- pushnum 5 # arg (noise)
- resume 1 1 # after coroutine ends, previous stack is back
- pushstatus
- return *
- ]]))
- assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
- assert(T.doremote(state, "return T") == '2')
- assert(T.doremote(state, "return A") == 'AA')
- assert(T.doremote(state, "return B") == 'BB')
- T.closestate(state)
- print'+'
- end
- -- leaving a pending coroutine open
- _G.TO_SURVIVE = coroutine.wrap(function ()
- local a = 10
- local x = function () a = a+1 end
- coroutine.yield()
- end)
- _G.TO_SURVIVE()
- if not _soft then
- -- bug (stack overflow)
- local lim = 1000000 -- stack limit; assume 32-bit machine
- local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1, lim + 5}
- for i = 1, #t do
- local j = t[i]
- local co = coroutine.create(function()
- return table.unpack({}, 1, j)
- end)
- local r, msg = coroutine.resume(co)
- -- must fail for unpacking larger than stack limit
- assert(j < lim or not r)
- end
- end
- assert(coroutine.running() == main)
- print"+"
- print"testing yields inside metamethods"
- local function val(x)
- if type(x) == "table" then return x.x else return x end
- end
- local mt = {
- __eq = function(a,b) coroutine.yield(nil, "eq"); return val(a) == val(b) end,
- __lt = function(a,b) coroutine.yield(nil, "lt"); return val(a) < val(b) end,
- __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
- __add = function(a,b) coroutine.yield(nil, "add");
- return val(a) + val(b) end,
- __sub = function(a,b) coroutine.yield(nil, "sub"); return val(a) - val(b) end,
- __mul = function(a,b) coroutine.yield(nil, "mul"); return val(a) * val(b) end,
- __div = function(a,b) coroutine.yield(nil, "div"); return val(a) / val(b) end,
- __idiv = function(a,b) coroutine.yield(nil, "idiv");
- return val(a) // val(b) end,
- __pow = function(a,b) coroutine.yield(nil, "pow"); return val(a) ^ val(b) end,
- __mod = function(a,b) coroutine.yield(nil, "mod"); return val(a) % val(b) end,
- __unm = function(a,b) coroutine.yield(nil, "unm"); return -val(a) end,
- __bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~val(a) end,
- __shl = function(a,b) coroutine.yield(nil, "shl");
- return val(a) << val(b) end,
- __shr = function(a,b) coroutine.yield(nil, "shr");
- return val(a) >> val(b) end,
- __band = function(a,b)
- coroutine.yield(nil, "band")
- return val(a) & val(b)
- end,
- __bor = function(a,b) coroutine.yield(nil, "bor");
- return val(a) | val(b) end,
- __bxor = function(a,b) coroutine.yield(nil, "bxor");
- return val(a) ~ val(b) end,
- __concat = function(a,b)
- coroutine.yield(nil, "concat");
- return val(a) .. val(b)
- end,
- __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
- __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
- }
- local function new (x)
- return setmetatable({x = x, k = {}}, mt)
- end
- local a = new(10)
- local b = new(12)
- local c = new"hello"
- local function run (f, t)
- local i = 1
- local c = coroutine.wrap(f)
- while true do
- local res, stat = c()
- if res then assert(t[i] == undef); return res, t end
- assert(stat == t[i])
- i = i + 1
- end
- end
- assert(run(function () if (a>=b) then return '>=' else return '<' end end,
- {"le", "sub"}) == "<")
- assert(run(function () if (a<=b) then return '<=' else return '>' end end,
- {"le", "sub"}) == "<=")
- assert(run(function () if (a==b) then return '==' else return '~=' end end,
- {"eq"}) == "~=")
- assert(run(function () return a & b + a end, {"add", "band"}) == 2)
- assert(run(function () return 1 + a end, {"add"}) == 11)
- assert(run(function () return a - 25 end, {"sub"}) == -15)
- assert(run(function () return 2 * a end, {"mul"}) == 20)
- assert(run(function () return a ^ 2 end, {"pow"}) == 100)
- assert(run(function () return a / 2 end, {"div"}) == 5)
- assert(run(function () return a % 6 end, {"mod"}) == 4)
- assert(run(function () return a // 3 end, {"idiv"}) == 3)
- assert(run(function () return a + b end, {"add"}) == 22)
- assert(run(function () return a - b end, {"sub"}) == -2)
- assert(run(function () return a * b end, {"mul"}) == 120)
- assert(run(function () return a ^ b end, {"pow"}) == 10^12)
- assert(run(function () return a / b end, {"div"}) == 10/12)
- assert(run(function () return a % b end, {"mod"}) == 10)
- assert(run(function () return a // b end, {"idiv"}) == 0)
- -- repeat tests with larger constants (to use 'K' opcodes)
- local a1000 = new(1000)
- assert(run(function () return a1000 + 1000 end, {"add"}) == 2000)
- assert(run(function () return a1000 - 25000 end, {"sub"}) == -24000)
- assert(run(function () return 2000 * a end, {"mul"}) == 20000)
- assert(run(function () return a1000 / 1000 end, {"div"}) == 1)
- assert(run(function () return a1000 % 600 end, {"mod"}) == 400)
- assert(run(function () return a1000 // 500 end, {"idiv"}) == 2)
- assert(run(function () return a % b end, {"mod"}) == 10)
- assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12)
- assert(run(function () return a | b end, {"bor"}) == 10 | 12)
- assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12)
- assert(run(function () return a << b end, {"shl"}) == 10 << 12)
- assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
- assert(run(function () return 10 & b end, {"band"}) == 10 & 12)
- assert(run(function () return a | 2 end, {"bor"}) == 10 | 2)
- assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2)
- assert(run(function () return a >> 2 end, {"shr"}) == 10 >> 2)
- assert(run(function () return 1 >> a end, {"shr"}) == 1 >> 10)
- assert(run(function () return a << 2 end, {"shl"}) == 10 << 2)
- assert(run(function () return 1 << a end, {"shl"}) == 1 << 10)
- assert(run(function () return 2 ~ a end, {"bxor"}) == 2 ~ 10)
- assert(run(function () return a..b end, {"concat"}) == "1012")
- assert(run(function() return a .. b .. c .. a end,
- {"concat", "concat", "concat"}) == "1012hello10")
- assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
- {"concat", "concat", "concat"}) == "ab10chello12x")
- do -- a few more tests for comparison operators
- local mt1 = {
- __le = function (a,b)
- coroutine.yield(10)
- return (val(a) <= val(b))
- end,
- __lt = function (a,b)
- coroutine.yield(10)
- return val(a) < val(b)
- end,
- }
- local mt2 = { __lt = mt1.__lt, __le = mt1.__le }
- local function run (f)
- local co = coroutine.wrap(f)
- local res
- repeat
- res = co()
- until res ~= 10
- return res
- end
- local function test ()
- local a1 = setmetatable({x=1}, mt1)
- local a2 = setmetatable({x=2}, mt2)
- assert(a1 < a2)
- assert(a1 <= a2)
- assert(1 < a2)
- assert(1 <= a2)
- assert(2 > a1)
- assert(2 >= a2)
- return true
- end
- run(test)
- end
- assert(run(function ()
- a.BB = print
- return a.BB
- end, {"nidx", "idx"}) == print)
- -- getuptable & setuptable
- do local _ENV = _ENV
- f = function () AAA = BBB + 1; return AAA end
- end
- local g = new(10); g.k.BBB = 10;
- debug.setupvalue(f, 1, g)
- assert(run(f, {"idx", "nidx", "idx"}) == 11)
- assert(g.k.AAA == 11)
- print"+"
- print"testing yields inside 'for' iterators"
- local f = function (s, i)
- if i%2 == 0 then coroutine.yield(nil, "for") end
- if i < s then return i + 1 end
- end
- assert(run(function ()
- local s = 0
- for i in f, 4, 0 do s = s + i end
- return s
- end, {"for", "for", "for"}) == 10)
- -- tests for coroutine API
- if T==nil then
- (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
- print "OK"; return
- end
- print('testing coroutine API')
- local function apico (...)
- local x = {...}
- return coroutine.wrap(function ()
- return T.testC(table.unpack(x))
- end)
- end
- local a = {apico(
- [[
- pushstring errorcode
- pcallk 1 0 2;
- invalid command (should not arrive here)
- ]],
- [[return *]],
- "stackmark",
- error
- )()}
- assert(#a == 4 and
- a[3] == "stackmark" and
- a[4] == "errorcode" and
- _G.status == "ERRRUN" and
- _G.ctx == 2) -- 'ctx' to pcallk
- local co = apico(
- "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
- coroutine.yield,
- "getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
- "getglobal status; getglobal ctx; return *")
- assert(co() == 10)
- assert(co(20, 30) == 'a')
- a = {co()}
- assert(#a == 10 and
- a[2] == coroutine.yield and
- a[5] == 20 and a[6] == 30 and
- a[7] == "YIELD" and a[8] == 3 and
- a[9] == "YIELD" and a[10] == 4)
- assert(not pcall(co)) -- coroutine is dead now
- f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
- co = coroutine.wrap(function ()
- assert(f() == 23); assert(f() == 23); return 10
- end)
- assert(co(23,16) == 5)
- assert(co(23,16) == 5)
- assert(co(23,16) == 10)
- -- testing coroutines with C bodies
- f = T.makeCfunc([[
- pushnum 102
- yieldk 1 U2
- cannot be here!
- ]],
- [[ # continuation
- pushvalue U3 # accessing upvalues inside a continuation
- pushvalue U4
- return *
- ]], 23, "huu")
- do -- testing bug introduced in commit f407b3c4a
- local X = false -- flag "to be closed"
- local coro = coroutine.wrap(T.testC)
- -- runs it until 'pcallk' (that yields)
- -- 4th argument (at index 4): object to be closed
- local res1, res2 = coro(
- [[
- toclose 3 # this could break the next 'pcallk'
- pushvalue 2 # push function 'yield' to call it
- pushint 22; pushint 33 # arguments to yield
- # calls 'yield' (2 args; 2 results; continuation function at index 4)
- pcallk 2 2 4
- invalid command (should not arrive here)
- ]], -- 1st argument (at index 1): code;
- coroutine.yield, -- (at index 2): function to be called
- func2close(function () X = true end), -- (index 3): TBC slot
- "pushint 43; return 3" -- (index 4): code for continuation function
- )
- assert(res1 == 22 and res2 == 33 and not X)
- local res1, res2, res3 = coro(34, "hi") -- runs continuation function
- assert(res1 == 34 and res2 == "hi" and res3 == 43 and X)
- end
- x = coroutine.wrap(f)
- assert(x() == 102)
- eqtab({x()}, {23, "huu"})
- f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
- a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
- pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
- return 4; ]], f)
- assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
- -- testing chain of suspendable C calls
- local count = 3 -- number of levels
- f = T.makeCfunc([[
- remove 1; # remove argument
- pushvalue U3; # get selection function
- call 0 1; # call it (result is 'f' or 'yield')
- pushstring hello # single argument for selected function
- pushupvalueindex 2; # index of continuation program
- callk 1 -1 .; # call selected function
- errorerror # should never arrive here
- ]],
- [[
- # continuation program
- pushnum 34 # return value
- return * # return all results
- ]],
- function () -- selection function
- count = count - 1
- if count == 0 then return coroutine.yield
- else return f
- end
- end
- )
- co = coroutine.wrap(function () return f(nil) end)
- assert(co() == "hello") -- argument to 'yield'
- a = {co()}
- -- three '34's (one from each pending C call)
- assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
- -- testing yields with continuations
- local y
- co = coroutine.wrap(function (...) return
- T.testC([[ # initial function
- yieldk 1 2
- cannot be here!
- ]],
- [[ # 1st continuation
- yieldk 0 3
- cannot be here!
- ]],
- [[ # 2nd continuation
- yieldk 0 4
- cannot be here!
- ]],
- [[ # 3th continuation
- pushvalue 6 # function which is last arg. to 'testC' here
- pushnum 10; pushnum 20;
- pcall 2 0 0 # call should throw an error and return to next line
- pop 1 # remove error message
- pushvalue 6
- getglobal status; getglobal ctx
- pcallk 2 2 5 # call should throw an error and jump to continuation
- cannot be here!
- ]],
- [[ # 4th (and last) continuation
- return *
- ]],
- -- function called by 3th continuation
- function (a,b) x=a; y=b; error("errmsg") end,
- ...
- )
- end)
- local a = {co(3,4,6)}
- assert(a[1] == 6 and a[2] == undef)
- a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 2)
- a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 3)
- a = {co(7,8)};
- -- original arguments
- assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
- type(a[3]) == 'string' and type(a[4]) == 'string' and
- type(a[5]) == 'string' and type(a[6]) == 'function')
- -- arguments left from fist resume
- assert(a[7] == 3 and a[8] == 4)
- -- arguments to last resume
- assert(a[9] == 7 and a[10] == 8)
- -- error message and nothing more
- assert(a[11]:find("errmsg") and #a == 11)
- -- check arguments to pcallk
- assert(x == "YIELD" and y == 4)
- assert(not pcall(co)) -- coroutine should be dead
- _G.ctx = nil
- _G.status = nil
- -- bug in nCcalls
- local co = coroutine.wrap(function ()
- local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
- return pcall(assert, table.unpack(a))
- end)
- local a = {co()}
- assert(a[10] == "hi")
- print'OK'
|