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