summaryrefslogtreecommitdiff
path: root/testes/coroutine.lua
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-17 14:46:37 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-17 14:46:37 -0200
commit063d4e4543088e7a21965bda8ee5a0f952a9029e (patch)
tree6c3f2f8e98c26f071a94a32f9f2754396a66a9de /testes/coroutine.lua
parente354c6355e7f48e087678ec49e340ca0696725b1 (diff)
downloadlua-5.3.5.tar.gz
lua-5.3.5.tar.bz2
lua-5.3.5.zip
Lua 5.3.5 ported to gitv5.3.5
This is the first commit for the branch Lua 5.3. All source files were copied from the official distribution of 5.3.5 in the Lua site. The test files are the same of 5.3.4. The manual came from the previous RCS repository, revision 1.167.1.2.
Diffstat (limited to 'testes/coroutine.lua')
-rw-r--r--testes/coroutine.lua874
1 files changed, 874 insertions, 0 deletions
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
new file mode 100644
index 00000000..c8fc8572
--- /dev/null
+++ b/testes/coroutine.lua
@@ -0,0 +1,874 @@
1-- $Id: coroutine.lua,v 1.42 2016/11/07 13:03:20 roberto Exp $
2-- See Copyright Notice in file all.lua
3
4print "testing coroutines"
5
6local debug = require'debug'
7
8local f
9
10local main, ismain = coroutine.running()
11assert(type(main) == "thread" and ismain)
12assert(not coroutine.resume(main))
13assert(not coroutine.isyieldable())
14assert(not pcall(coroutine.yield))
15
16
17-- trivial errors
18assert(not pcall(coroutine.resume, 0))
19assert(not pcall(coroutine.status, 0))
20
21
22-- tests for multiple yield/resume arguments
23
24local function eqtab (t1, t2)
25 assert(#t1 == #t2)
26 for i = 1, #t1 do
27 local v = t1[i]
28 assert(t2[i] == v)
29 end
30end
31
32_G.x = nil -- declare x
33function foo (a, ...)
34 local x, y = coroutine.running()
35 assert(x == f and y == false)
36 -- next call should not corrupt coroutine (but must fail,
37 -- as it attempts to resume the running coroutine)
38 assert(coroutine.resume(f) == false)
39 assert(coroutine.status(f) == "running")
40 local arg = {...}
41 assert(coroutine.isyieldable())
42 for i=1,#arg do
43 _G.x = {coroutine.yield(table.unpack(arg[i]))}
44 end
45 return table.unpack(a)
46end
47
48f = coroutine.create(foo)
49assert(type(f) == "thread" and coroutine.status(f) == "suspended")
50assert(string.find(tostring(f), "thread"))
51local s,a,b,c,d
52s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
53assert(s and a == nil and coroutine.status(f) == "suspended")
54s,a,b,c,d = coroutine.resume(f)
55eqtab(_G.x, {})
56assert(s and a == 1 and b == nil)
57s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
58eqtab(_G.x, {1, 2, 3})
59assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
60s,a,b,c,d = coroutine.resume(f, "xuxu")
61eqtab(_G.x, {"xuxu"})
62assert(s and a == 1 and b == 2 and c == 3 and d == nil)
63assert(coroutine.status(f) == "dead")
64s, a = coroutine.resume(f, "xuxu")
65assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
66
67
68-- yields in tail calls
69local function foo (i) return coroutine.yield(i) end
70f = coroutine.wrap(function ()
71 for i=1,10 do
72 assert(foo(i) == _G.x)
73 end
74 return 'a'
75end)
76for i=1,10 do _G.x = i; assert(f(i) == i) end
77_G.x = 'xuxu'; assert(f('xuxu') == 'a')
78
79-- recursive
80function pf (n, i)
81 coroutine.yield(n)
82 pf(n*i, i+1)
83end
84
85f = coroutine.wrap(pf)
86local s=1
87for i=1,10 do
88 assert(f(1, 1) == s)
89 s = s*i
90end
91
92-- sieve
93function gen (n)
94 return coroutine.wrap(function ()
95 for i=2,n do coroutine.yield(i) end
96 end)
97end
98
99
100function filter (p, g)
101 return coroutine.wrap(function ()
102 while 1 do
103 local n = g()
104 if n == nil then return end
105 if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
106 end
107 end)
108end
109
110local x = gen(100)
111local a = {}
112while 1 do
113 local n = x()
114 if n == nil then break end
115 table.insert(a, n)
116 x = filter(n, x)
117end
118
119assert(#a == 25 and a[#a] == 97)
120x, a = nil
121
122-- yielding across C boundaries
123
124co = coroutine.wrap(function()
125 assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
126 assert(coroutine.isyieldable())
127 coroutine.yield(20)
128 return 30
129 end)
130
131assert(co() == 20)
132assert(co() == 30)
133
134
135local f = function (s, i) return coroutine.yield(i) end
136
137local f1 = coroutine.wrap(function ()
138 return xpcall(pcall, function (...) return ... end,
139 function ()
140 local s = 0
141 for i in f, nil, 1 do pcall(function () s = s + i end) end
142 error({s})
143 end)
144 end)
145
146f1()
147for i = 1, 10 do assert(f1(i) == i) end
148local r1, r2, v = f1(nil)
149assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
150
151
152function f (a, b) a = coroutine.yield(a); error{a + b} end
153function g(x) return x[1]*2 end
154
155co = coroutine.wrap(function ()
156 coroutine.yield(xpcall(f, g, 10, 20))
157 end)
158
159assert(co() == 10)
160r, msg = co(100)
161assert(not r and msg == 240)
162
163
164-- unyieldable C call
165do
166 local function f (c)
167 assert(not coroutine.isyieldable())
168 return c .. c
169 end
170
171 local co = coroutine.wrap(function (c)
172 assert(coroutine.isyieldable())
173 local s = string.gsub("a", ".", f)
174 return s
175 end)
176 assert(co() == "aa")
177end
178
179
180-- errors in coroutines
181function foo ()
182 assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
183 assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
184 coroutine.yield(3)
185 error(foo)
186end
187
188function goo() foo() end
189x = coroutine.wrap(goo)
190assert(x() == 3)
191local a,b = pcall(x)
192assert(not a and b == foo)
193
194x = coroutine.create(goo)
195a,b = coroutine.resume(x)
196assert(a and b == 3)
197a,b = coroutine.resume(x)
198assert(not a and b == foo and coroutine.status(x) == "dead")
199a,b = coroutine.resume(x)
200assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
201
202
203-- co-routines x for loop
204function all (a, n, k)
205 if k == 0 then coroutine.yield(a)
206 else
207 for i=1,n do
208 a[k] = i
209 all(a, n, k-1)
210 end
211 end
212end
213
214local a = 0
215for t in coroutine.wrap(function () all({}, 5, 4) end) do
216 a = a+1
217end
218assert(a == 5^4)
219
220
221-- access to locals of collected corroutines
222local C = {}; setmetatable(C, {__mode = "kv"})
223local x = coroutine.wrap (function ()
224 local a = 10
225 local function f () a = a+10; return a end
226 while true do
227 a = a+1
228 coroutine.yield(f)
229 end
230 end)
231
232C[1] = x;
233
234local f = x()
235assert(f() == 21 and x()() == 32 and x() == f)
236x = nil
237collectgarbage()
238assert(C[1] == nil)
239assert(f() == 43 and f() == 53)
240
241
242-- old bug: attempt to resume itself
243
244function co_func (current_co)
245 assert(coroutine.running() == current_co)
246 assert(coroutine.resume(current_co) == false)
247 coroutine.yield(10, 20)
248 assert(coroutine.resume(current_co) == false)
249 coroutine.yield(23)
250 return 10
251end
252
253local co = coroutine.create(co_func)
254local a,b,c = coroutine.resume(co, co)
255assert(a == true and b == 10 and c == 20)
256a,b = coroutine.resume(co, co)
257assert(a == true and b == 23)
258a,b = coroutine.resume(co, co)
259assert(a == true and b == 10)
260assert(coroutine.resume(co, co) == false)
261assert(coroutine.resume(co, co) == false)
262
263
264-- other old bug when attempting to resume itself
265-- (trigger C-code assertions)
266do
267 local A = coroutine.running()
268 local B = coroutine.create(function() return coroutine.resume(A) end)
269 local st, res = coroutine.resume(B)
270 assert(st == true and res == false)
271
272 A = coroutine.wrap(function() return pcall(A, 1) end)
273 st, res = A()
274 assert(not st and string.find(res, "non%-suspended"))
275end
276
277
278-- attempt to resume 'normal' coroutine
279local co1, co2
280co1 = coroutine.create(function () return co2() end)
281co2 = coroutine.wrap(function ()
282 assert(coroutine.status(co1) == 'normal')
283 assert(not coroutine.resume(co1))
284 coroutine.yield(3)
285 end)
286
287a,b = coroutine.resume(co1)
288assert(a and b == 3)
289assert(coroutine.status(co1) == 'dead')
290
291-- infinite recursion of coroutines
292a = function(a) coroutine.wrap(a)(a) end
293assert(not pcall(a, a))
294a = nil
295
296
297-- access to locals of erroneous coroutines
298local x = coroutine.create (function ()
299 local a = 10
300 _G.f = function () a=a+1; return a end
301 error('x')
302 end)
303
304assert(not coroutine.resume(x))
305-- overwrite previous position of local `a'
306assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
307assert(_G.f() == 11)
308assert(_G.f() == 12)
309
310
311if not T then
312 (Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n')
313else
314 print "testing yields inside hooks"
315
316 local turn
317
318 function fact (t, x)
319 assert(turn == t)
320 if x == 0 then return 1
321 else return x*fact(t, x-1)
322 end
323 end
324
325 local A, B = 0, 0
326
327 local x = coroutine.create(function ()
328 T.sethook("yield 0", "", 2)
329 A = fact("A", 6)
330 end)
331
332 local y = coroutine.create(function ()
333 T.sethook("yield 0", "", 3)
334 B = fact("B", 7)
335 end)
336
337 while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
338 if A==0 then turn = "A"; assert(T.resume(x)) end
339 if B==0 then turn = "B"; assert(T.resume(y)) end
340 end
341
342 assert(B // A == 7) -- fact(7) // fact(6)
343
344 local line = debug.getinfo(1, "l").currentline + 2 -- get line number
345 local function foo ()
346 local x = 10 --<< this line is 'line'
347 x = x + 10
348 _G.XX = x
349 end
350
351 -- testing yields in line hook
352 local co = coroutine.wrap(function ()
353 T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
354
355 _G.XX = nil;
356 _G.X = nil; co(); assert(_G.X == line)
357 _G.X = nil; co(); assert(_G.X == line + 1)
358 _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
359 _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
360 assert(co() == 10)
361
362 -- testing yields in count hook
363 co = coroutine.wrap(function ()
364 T.sethook("yield 0", "", 1); foo(); return 10 end)
365
366 _G.XX = nil;
367 local c = 0
368 repeat c = c + 1; local a = co() until a == 10
369 assert(_G.XX == 20 and c >= 5)
370
371 co = coroutine.wrap(function ()
372 T.sethook("yield 0", "", 2); foo(); return 10 end)
373
374 _G.XX = nil;
375 local c = 0
376 repeat c = c + 1; local a = co() until a == 10
377 assert(_G.XX == 20 and c >= 5)
378 _G.X = nil; _G.XX = nil
379
380 do
381 -- testing debug library on a coroutine suspended inside a hook
382 -- (bug in 5.2/5.3)
383 c = coroutine.create(function (a, ...)
384 T.sethook("yield 0", "l") -- will yield on next two lines
385 assert(a == 10)
386 return ...
387 end)
388
389 assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
390 local n,v = debug.getlocal(c, 0, 1) -- check its local
391 assert(n == "a" and v == 1)
392 n,v = debug.getlocal(c, 0, -1) -- check varargs
393 assert(v == 2)
394 n,v = debug.getlocal(c, 0, -2)
395 assert(v == 3)
396 assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
397 assert(debug.setlocal(c, 0, -2, 20))
398 local t = debug.getinfo(c, 0) -- test 'getinfo'
399 assert(t.currentline == t.linedefined + 1)
400 assert(not debug.getinfo(c, 1)) -- no other level
401 assert(coroutine.resume(c)) -- run next line
402 v = {coroutine.resume(c)} -- finish coroutine
403 assert(v[1] == true and v[2] == 2 and v[3] == 20 and v[4] == nil)
404 assert(not coroutine.resume(c))
405 end
406
407 do
408 -- testing debug library on last function in a suspended coroutine
409 -- (bug in 5.2/5.3)
410 local c = coroutine.create(function () T.testC("yield 1", 10, 20) end)
411 local a, b = coroutine.resume(c)
412 assert(a and b == 20)
413 assert(debug.getinfo(c, 0).linedefined == -1)
414 a, b = debug.getlocal(c, 0, 2)
415 assert(b == 10)
416 end
417
418
419 print "testing coroutine API"
420
421 -- reusing a thread
422 assert(T.testC([[
423 newthread # create thread
424 pushvalue 2 # push body
425 pushstring 'a a a' # push argument
426 xmove 0 3 2 # move values to new thread
427 resume -1, 1 # call it first time
428 pushstatus
429 xmove 3 0 0 # move results back to stack
430 setglobal X # result
431 setglobal Y # status
432 pushvalue 2 # push body (to call it again)
433 pushstring 'b b b'
434 xmove 0 3 2
435 resume -1, 1 # call it again
436 pushstatus
437 xmove 3 0 0
438 return 1 # return result
439 ]], function (...) return ... end) == 'b b b')
440
441 assert(X == 'a a a' and Y == 'OK')
442
443
444 -- resuming running coroutine
445 C = coroutine.create(function ()
446 return T.testC([[
447 pushnum 10;
448 pushnum 20;
449 resume -3 2;
450 pushstatus
451 gettop;
452 return 3]], C)
453 end)
454 local a, b, c, d = coroutine.resume(C)
455 assert(a == true and string.find(b, "non%-suspended") and
456 c == "ERRRUN" and d == 4)
457
458 a, b, c, d = T.testC([[
459 rawgeti R 1 # get main thread
460 pushnum 10;
461 pushnum 20;
462 resume -3 2;
463 pushstatus
464 gettop;
465 return 4]])
466 assert(a == coroutine.running() and string.find(b, "non%-suspended") and
467 c == "ERRRUN" and d == 4)
468
469
470 -- using a main thread as a coroutine
471 local state = T.newstate()
472 T.loadlib(state)
473
474 assert(T.doremote(state, [[
475 coroutine = require'coroutine';
476 X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
477 return 'ok']]))
478
479 t = table.pack(T.testC(state, [[
480 rawgeti R 1 # get main thread
481 pushstring 'XX'
482 getglobal X # get function for body
483 pushstring AA # arg
484 resume 1 1 # 'resume' shadows previous stack!
485 gettop
486 setglobal T # top
487 setglobal B # second yielded value
488 setglobal A # fist yielded value
489 rawgeti R 1 # get main thread
490 pushnum 5 # arg (noise)
491 resume 1 1 # after coroutine ends, previous stack is back
492 pushstatus
493 return *
494 ]]))
495 assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
496 assert(T.doremote(state, "return T") == '2')
497 assert(T.doremote(state, "return A") == 'AA')
498 assert(T.doremote(state, "return B") == 'BB')
499
500 T.closestate(state)
501
502 print'+'
503
504end
505
506
507-- leaving a pending coroutine open
508_X = coroutine.wrap(function ()
509 local a = 10
510 local x = function () a = a+1 end
511 coroutine.yield()
512 end)
513
514_X()
515
516
517if not _soft then
518 -- bug (stack overflow)
519 local j = 2^9
520 local lim = 1000000 -- (C stack limit; assume 32-bit machine)
521 local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
522 for i = 1, #t do
523 local j = t[i]
524 co = coroutine.create(function()
525 local t = {}
526 for i = 1, j do t[i] = i end
527 return table.unpack(t)
528 end)
529 local r, msg = coroutine.resume(co)
530 assert(not r)
531 end
532 co = nil
533end
534
535
536assert(coroutine.running() == main)
537
538print"+"
539
540
541print"testing yields inside metamethods"
542
543local mt = {
544 __eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end,
545 __lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end,
546 __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
547 __add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end,
548 __sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end,
549 __mod = function(a,b) coroutine.yield(nil, "mod"); return a.x % b.x end,
550 __unm = function(a,b) coroutine.yield(nil, "unm"); return -a.x end,
551 __bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~a.x end,
552 __shl = function(a,b) coroutine.yield(nil, "shl"); return a.x << b.x end,
553 __shr = function(a,b) coroutine.yield(nil, "shr"); return a.x >> b.x end,
554 __band = function(a,b)
555 a = type(a) == "table" and a.x or a
556 b = type(b) == "table" and b.x or b
557 coroutine.yield(nil, "band")
558 return a & b
559 end,
560 __bor = function(a,b) coroutine.yield(nil, "bor"); return a.x | b.x end,
561 __bxor = function(a,b) coroutine.yield(nil, "bxor"); return a.x ~ b.x end,
562
563 __concat = function(a,b)
564 coroutine.yield(nil, "concat");
565 a = type(a) == "table" and a.x or a
566 b = type(b) == "table" and b.x or b
567 return a .. b
568 end,
569 __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
570 __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
571}
572
573
574local function new (x)
575 return setmetatable({x = x, k = {}}, mt)
576end
577
578
579local a = new(10)
580local b = new(12)
581local c = new"hello"
582
583local function run (f, t)
584 local i = 1
585 local c = coroutine.wrap(f)
586 while true do
587 local res, stat = c()
588 if res then assert(t[i] == nil); return res, t end
589 assert(stat == t[i])
590 i = i + 1
591 end
592end
593
594
595assert(run(function () if (a>=b) then return '>=' else return '<' end end,
596 {"le", "sub"}) == "<")
597-- '<=' using '<'
598mt.__le = nil
599assert(run(function () if (a<=b) then return '<=' else return '>' end end,
600 {"lt"}) == "<=")
601assert(run(function () if (a==b) then return '==' else return '~=' end end,
602 {"eq"}) == "~=")
603
604assert(run(function () return a & b + a end, {"add", "band"}) == 2)
605
606assert(run(function () return a % b end, {"mod"}) == 10)
607
608assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12)
609assert(run(function () return a | b end, {"bor"}) == 10 | 12)
610assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12)
611assert(run(function () return a << b end, {"shl"}) == 10 << 12)
612assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
613
614assert(run(function () return a..b end, {"concat"}) == "1012")
615
616assert(run(function() return a .. b .. c .. a end,
617 {"concat", "concat", "concat"}) == "1012hello10")
618
619assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
620 {"concat", "concat", "concat"}) == "ab10chello12x")
621
622
623do -- a few more tests for comparsion operators
624 local mt1 = {
625 __le = function (a,b)
626 coroutine.yield(10)
627 return
628 (type(a) == "table" and a.x or a) <= (type(b) == "table" and b.x or b)
629 end,
630 __lt = function (a,b)
631 coroutine.yield(10)
632 return
633 (type(a) == "table" and a.x or a) < (type(b) == "table" and b.x or b)
634 end,
635 }
636 local mt2 = { __lt = mt1.__lt } -- no __le
637
638 local function run (f)
639 local co = coroutine.wrap(f)
640 local res
641 repeat
642 res = co()
643 until res ~= 10
644 return res
645 end
646
647 local function test ()
648 local a1 = setmetatable({x=1}, mt1)
649 local a2 = setmetatable({x=2}, mt2)
650 assert(a1 < a2)
651 assert(a1 <= a2)
652 assert(1 < a2)
653 assert(1 <= a2)
654 assert(2 > a1)
655 assert(2 >= a2)
656 return true
657 end
658
659 run(test)
660
661end
662
663assert(run(function ()
664 a.BB = print
665 return a.BB
666 end, {"nidx", "idx"}) == print)
667
668-- getuptable & setuptable
669do local _ENV = _ENV
670 f = function () AAA = BBB + 1; return AAA end
671end
672g = new(10); g.k.BBB = 10;
673debug.setupvalue(f, 1, g)
674assert(run(f, {"idx", "nidx", "idx"}) == 11)
675assert(g.k.AAA == 11)
676
677print"+"
678
679print"testing yields inside 'for' iterators"
680
681local f = function (s, i)
682 if i%2 == 0 then coroutine.yield(nil, "for") end
683 if i < s then return i + 1 end
684 end
685
686assert(run(function ()
687 local s = 0
688 for i in f, 4, 0 do s = s + i end
689 return s
690 end, {"for", "for", "for"}) == 10)
691
692
693
694-- tests for coroutine API
695if T==nil then
696 (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
697 return
698end
699
700print('testing coroutine API')
701
702local function apico (...)
703 local x = {...}
704 return coroutine.wrap(function ()
705 return T.testC(table.unpack(x))
706 end)
707end
708
709local a = {apico(
710[[
711 pushstring errorcode
712 pcallk 1 0 2;
713 invalid command (should not arrive here)
714]],
715[[return *]],
716"stackmark",
717error
718)()}
719assert(#a == 4 and
720 a[3] == "stackmark" and
721 a[4] == "errorcode" and
722 _G.status == "ERRRUN" and
723 _G.ctx == 2) -- 'ctx' to pcallk
724
725local co = apico(
726 "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
727 coroutine.yield,
728 "getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
729 "getglobal status; getglobal ctx; return *")
730
731assert(co() == 10)
732assert(co(20, 30) == 'a')
733a = {co()}
734assert(#a == 10 and
735 a[2] == coroutine.yield and
736 a[5] == 20 and a[6] == 30 and
737 a[7] == "YIELD" and a[8] == 3 and
738 a[9] == "YIELD" and a[10] == 4)
739assert(not pcall(co)) -- coroutine is dead now
740
741
742f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
743co = coroutine.wrap(function ()
744 assert(f() == 23); assert(f() == 23); return 10
745end)
746assert(co(23,16) == 5)
747assert(co(23,16) == 5)
748assert(co(23,16) == 10)
749
750
751-- testing coroutines with C bodies
752f = T.makeCfunc([[
753 pushnum 102
754 yieldk 1 U2
755 cannot be here!
756]],
757[[ # continuation
758 pushvalue U3 # accessing upvalues inside a continuation
759 pushvalue U4
760 return *
761]], 23, "huu")
762
763x = coroutine.wrap(f)
764assert(x() == 102)
765eqtab({x()}, {23, "huu"})
766
767
768f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
769
770a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
771 pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
772 return 4; ]], f)
773
774assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
775
776
777-- testing chain of suspendable C calls
778
779local count = 3 -- number of levels
780
781f = T.makeCfunc([[
782 remove 1; # remove argument
783 pushvalue U3; # get selection function
784 call 0 1; # call it (result is 'f' or 'yield')
785 pushstring hello # single argument for selected function
786 pushupvalueindex 2; # index of continuation program
787 callk 1 -1 .; # call selected function
788 errorerror # should never arrive here
789]],
790[[
791 # continuation program
792 pushnum 34 # return value
793 return * # return all results
794]],
795function () -- selection function
796 count = count - 1
797 if count == 0 then return coroutine.yield
798 else return f
799 end
800end
801)
802
803co = coroutine.wrap(function () return f(nil) end)
804assert(co() == "hello") -- argument to 'yield'
805a = {co()}
806-- three '34's (one from each pending C call)
807assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
808
809
810-- testing yields with continuations
811
812co = coroutine.wrap(function (...) return
813 T.testC([[ # initial function
814 yieldk 1 2
815 cannot be here!
816 ]],
817 [[ # 1st continuation
818 yieldk 0 3
819 cannot be here!
820 ]],
821 [[ # 2nd continuation
822 yieldk 0 4
823 cannot be here!
824 ]],
825 [[ # 3th continuation
826 pushvalue 6 # function which is last arg. to 'testC' here
827 pushnum 10; pushnum 20;
828 pcall 2 0 0 # call should throw an error and return to next line
829 pop 1 # remove error message
830 pushvalue 6
831 getglobal status; getglobal ctx
832 pcallk 2 2 5 # call should throw an error and jump to continuation
833 cannot be here!
834 ]],
835 [[ # 4th (and last) continuation
836 return *
837 ]],
838 -- function called by 3th continuation
839 function (a,b) x=a; y=b; error("errmsg") end,
840 ...
841)
842end)
843
844local a = {co(3,4,6)}
845assert(a[1] == 6 and a[2] == nil)
846a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 2)
847a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 3)
848a = {co(7,8)};
849-- original arguments
850assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
851 type(a[3]) == 'string' and type(a[4]) == 'string' and
852 type(a[5]) == 'string' and type(a[6]) == 'function')
853-- arguments left from fist resume
854assert(a[7] == 3 and a[8] == 4)
855-- arguments to last resume
856assert(a[9] == 7 and a[10] == 8)
857-- error message and nothing more
858assert(a[11]:find("errmsg") and #a == 11)
859-- check arguments to pcallk
860assert(x == "YIELD" and y == 4)
861
862assert(not pcall(co)) -- coroutine should be dead
863
864
865-- bug in nCcalls
866local co = coroutine.wrap(function ()
867 local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
868 return pcall(assert, table.unpack(a))
869end)
870
871local a = {co()}
872assert(a[10] == "hi")
873
874print'OK'