summaryrefslogtreecommitdiff
path: root/testes/coroutine.lua
diff options
context:
space:
mode:
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'