coroutine.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. print "testing coroutines"
  2. local debug = require'debug'
  3. local f
  4. local main, ismain = coroutine.running()
  5. assert(type(main) == "thread" and ismain)
  6. assert(not coroutine.resume(main))
  7. assert(not pcall(coroutine.yield))
  8. -- tests for multiple yield/resume arguments
  9. local function eqtab (t1, t2)
  10. assert(#t1 == #t2)
  11. for i = 1, #t1 do
  12. local v = t1[i]
  13. assert(t2[i] == v)
  14. end
  15. end
  16. _G.x = nil -- declare x
  17. function foo (a, ...)
  18. local x, y = coroutine.running()
  19. assert(x == f and y == false)
  20. assert(coroutine.status(f) == "running")
  21. local arg = {...}
  22. for i=1,#arg do
  23. _G.x = {coroutine.yield(table.unpack(arg[i]))}
  24. end
  25. return table.unpack(a)
  26. end
  27. f = coroutine.create(foo)
  28. assert(type(f) == "thread" and coroutine.status(f) == "suspended")
  29. assert(string.find(tostring(f), "thread"))
  30. local s,a,b,c,d
  31. s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
  32. assert(s and a == nil and coroutine.status(f) == "suspended")
  33. s,a,b,c,d = coroutine.resume(f)
  34. eqtab(_G.x, {})
  35. assert(s and a == 1 and b == nil)
  36. s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
  37. eqtab(_G.x, {1, 2, 3})
  38. assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
  39. s,a,b,c,d = coroutine.resume(f, "xuxu")
  40. eqtab(_G.x, {"xuxu"})
  41. assert(s and a == 1 and b == 2 and c == 3 and d == nil)
  42. assert(coroutine.status(f) == "dead")
  43. s, a = coroutine.resume(f, "xuxu")
  44. assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
  45. -- yields in tail calls
  46. local function foo (i) return coroutine.yield(i) end
  47. f = coroutine.wrap(function ()
  48. for i=1,10 do
  49. assert(foo(i) == _G.x)
  50. end
  51. return 'a'
  52. end)
  53. for i=1,10 do _G.x = i; assert(f(i) == i) end
  54. _G.x = 'xuxu'; assert(f('xuxu') == 'a')
  55. -- recursive
  56. function pf (n, i)
  57. coroutine.yield(n)
  58. pf(n*i, i+1)
  59. end
  60. f = coroutine.wrap(pf)
  61. local s=1
  62. for i=1,10 do
  63. assert(f(1, 1) == s)
  64. s = s*i
  65. end
  66. -- sieve
  67. function gen (n)
  68. return coroutine.wrap(function ()
  69. for i=2,n do coroutine.yield(i) end
  70. end)
  71. end
  72. function filter (p, g)
  73. return coroutine.wrap(function ()
  74. while 1 do
  75. local n = g()
  76. if n == nil then return end
  77. if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
  78. end
  79. end)
  80. end
  81. local x = gen(100)
  82. local a = {}
  83. while 1 do
  84. local n = x()
  85. if n == nil then break end
  86. table.insert(a, n)
  87. x = filter(n, x)
  88. end
  89. assert(#a == 25 and a[#a] == 97)
  90. -- yielding across C boundaries
  91. co = coroutine.wrap(function()
  92. assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
  93. coroutine.yield(20)
  94. return 30
  95. end)
  96. assert(co() == 20)
  97. assert(co() == 30)
  98. local f = function (s, i) return coroutine.yield(i) end
  99. local f1 = coroutine.wrap(function ()
  100. return xpcall(pcall, function (...) return ... end,
  101. function ()
  102. local s = 0
  103. for i in f, nil, 1 do pcall(function () s = s + i end) end
  104. error({s})
  105. end)
  106. end)
  107. f1()
  108. for i = 1, 10 do assert(f1(i) == i) end
  109. local r1, r2, v = f1(nil)
  110. assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
  111. function f (a, b) a = coroutine.yield(a); error{a + b} end
  112. function g(x) return x[1]*2 end
  113. co = coroutine.wrap(function ()
  114. coroutine.yield(xpcall(f, g, 10, 20))
  115. end)
  116. assert(co() == 10)
  117. r, msg = co(100)
  118. assert(not r and msg == 240)
  119. -- errors in coroutines
  120. function foo ()
  121. assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
  122. assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
  123. coroutine.yield(3)
  124. error(foo)
  125. end
  126. function goo() foo() end
  127. x = coroutine.wrap(goo)
  128. assert(x() == 3)
  129. local a,b = pcall(x)
  130. assert(not a and b == foo)
  131. x = coroutine.create(goo)
  132. a,b = coroutine.resume(x)
  133. assert(a and b == 3)
  134. a,b = coroutine.resume(x)
  135. assert(not a and b == foo and coroutine.status(x) == "dead")
  136. a,b = coroutine.resume(x)
  137. assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
  138. -- co-routines x for loop
  139. function all (a, n, k)
  140. if k == 0 then coroutine.yield(a)
  141. else
  142. for i=1,n do
  143. a[k] = i
  144. all(a, n, k-1)
  145. end
  146. end
  147. end
  148. local a = 0
  149. for t in coroutine.wrap(function () all({}, 5, 4) end) do
  150. a = a+1
  151. end
  152. assert(a == 5^4)
  153. -- access to locals of collected corroutines
  154. local C = {}; setmetatable(C, {__mode = "kv"})
  155. local x = coroutine.wrap (function ()
  156. local a = 10
  157. local function f () a = a+10; return a end
  158. while true do
  159. a = a+1
  160. coroutine.yield(f)
  161. end
  162. end)
  163. C[1] = x;
  164. local f = x()
  165. assert(f() == 21 and x()() == 32 and x() == f)
  166. x = nil
  167. collectgarbage()
  168. assert(C[1] == nil)
  169. assert(f() == 43 and f() == 53)
  170. -- old bug: attempt to resume itself
  171. function co_func (current_co)
  172. assert(coroutine.running() == current_co)
  173. assert(coroutine.resume(current_co) == false)
  174. assert(coroutine.resume(current_co) == false)
  175. return 10
  176. end
  177. local co = coroutine.create(co_func)
  178. local a,b = coroutine.resume(co, co)
  179. assert(a == true and b == 10)
  180. assert(coroutine.resume(co, co) == false)
  181. assert(coroutine.resume(co, co) == false)
  182. -- attempt to resume 'normal' coroutine
  183. co1 = coroutine.create(function () return co2() end)
  184. co2 = coroutine.wrap(function ()
  185. assert(coroutine.status(co1) == 'normal')
  186. assert(not coroutine.resume(co1))
  187. coroutine.yield(3)
  188. end)
  189. a,b = coroutine.resume(co1)
  190. assert(a and b == 3)
  191. assert(coroutine.status(co1) == 'dead')
  192. -- infinite recursion of coroutines
  193. a = function(a) coroutine.wrap(a)(a) end
  194. assert(not pcall(a, a))
  195. -- access to locals of erroneous coroutines
  196. local x = coroutine.create (function ()
  197. local a = 10
  198. _G.f = function () a=a+1; return a end
  199. error('x')
  200. end)
  201. assert(not coroutine.resume(x))
  202. -- overwrite previous position of local `a'
  203. assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
  204. assert(_G.f() == 11)
  205. assert(_G.f() == 12)
  206. if not T then
  207. (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a')
  208. else
  209. print "testing yields inside hooks"
  210. local turn
  211. function fact (t, x)
  212. assert(turn == t)
  213. if x == 0 then return 1
  214. else return x*fact(t, x-1)
  215. end
  216. end
  217. local A,B,a,b = 0,0,0,0
  218. local x = coroutine.create(function ()
  219. T.sethook("yield 0", "", 2)
  220. A = fact("A", 10)
  221. end)
  222. local y = coroutine.create(function ()
  223. T.sethook("yield 0", "", 3)
  224. B = fact("B", 11)
  225. end)
  226. while A==0 or B==0 do
  227. if A==0 then turn = "A"; assert(T.resume(x)) end
  228. if B==0 then turn = "B"; assert(T.resume(y)) end
  229. end
  230. assert(B/A == 11)
  231. local line = debug.getinfo(1, "l").currentline + 2 -- get line number
  232. local function foo ()
  233. local x = 10 --<< this line is 'line'
  234. x = x + 10
  235. _G.XX = x
  236. end
  237. -- testing yields in line hook
  238. local co = coroutine.wrap(function ()
  239. T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
  240. _G.XX = nil;
  241. _G.X = nil; co(); assert(_G.X == line)
  242. _G.X = nil; co(); assert(_G.X == line + 1)
  243. _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
  244. _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
  245. assert(co() == 10)
  246. -- testing yields in count hook
  247. co = coroutine.wrap(function ()
  248. T.sethook("yield 0", "", 1); foo(); return 10 end)
  249. _G.XX = nil;
  250. local c = 0
  251. repeat c = c + 1; local a = co() until a == 10
  252. assert(_G.XX == 20 and c == 10)
  253. co = coroutine.wrap(function ()
  254. T.sethook("yield 0", "", 2); foo(); return 10 end)
  255. _G.XX = nil;
  256. local c = 0
  257. repeat c = c + 1; local a = co() until a == 10
  258. assert(_G.XX == 20 and c == 5)
  259. _G.X = nil; _G.XX = nil
  260. print "testing coroutine API"
  261. -- reusing a thread
  262. assert(T.testC([[
  263. newthread # create thread
  264. pushvalue 2 # push body
  265. pushstring 'a a a' # push argument
  266. xmove 0 3 2 # move values to new thread
  267. resume -1, 1 # call it first time
  268. pushstatus
  269. xmove 3 0 0 # move results back to stack
  270. setglobal X # result
  271. setglobal Y # status
  272. pushvalue 2 # push body (to call it again)
  273. pushstring 'b b b'
  274. xmove 0 3 2
  275. resume -1, 1 # call it again
  276. pushstatus
  277. xmove 3 0 0
  278. return 1 # return result
  279. ]], function (...) return ... end) == 'b b b')
  280. assert(X == 'a a a' and Y == 'OK')
  281. -- resuming running coroutine
  282. C = coroutine.create(function ()
  283. return T.testC([[
  284. pushnum 10;
  285. pushnum 20;
  286. resume -3 2;
  287. pushstatus
  288. gettop;
  289. return 3]], C)
  290. end)
  291. local a, b, c, d = coroutine.resume(C)
  292. assert(a == true and string.find(b, "non%-suspended") and
  293. c == "ERRRUN" and d == 4)
  294. a, b, c, d = T.testC([[
  295. rawgeti R 1 # get main thread
  296. pushnum 10;
  297. pushnum 20;
  298. resume -3 2;
  299. pushstatus
  300. gettop;
  301. return 4]])
  302. assert(a == coroutine.running() and string.find(b, "non%-suspended") and
  303. c == "ERRRUN" and d == 4)
  304. -- using a main thread as a coroutine
  305. local state = T.newstate()
  306. T.loadlib(state)
  307. assert(T.doremote(state, [[
  308. coroutine = require'coroutine';
  309. X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
  310. return 'ok']]))
  311. t = table.pack(T.testC(state, [[
  312. rawgeti R 1 # get main thread
  313. pushstring 'XX'
  314. getglobal X # get function for body
  315. pushstring AA # arg
  316. resume 1 1 # 'resume' shadows previous stack!
  317. gettop
  318. setglobal T # top
  319. setglobal B # second yielded value
  320. setglobal A # fist yielded value
  321. rawgeti R 1 # get main thread
  322. pushnum 5 # arg (noise)
  323. resume 1 1 # after coroutine ends, previous stack is back
  324. pushstatus
  325. gettop
  326. return .
  327. ]]))
  328. assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
  329. assert(T.doremote(state, "return T") == '2')
  330. assert(T.doremote(state, "return A") == 'AA')
  331. assert(T.doremote(state, "return B") == 'BB')
  332. T.closestate(state)
  333. print'+'
  334. end
  335. -- leaving a pending coroutine open
  336. _X = coroutine.wrap(function ()
  337. local a = 10
  338. local x = function () a = a+1 end
  339. coroutine.yield()
  340. end)
  341. _X()
  342. if not _soft then
  343. -- bug (stack overflow)
  344. local j = 2^9
  345. local lim = 1000000 -- (C stack limit; assume 32-bit machine)
  346. local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
  347. for i = 1, #t do
  348. local j = t[i]
  349. co = coroutine.create(function()
  350. local t = {}
  351. for i = 1, j do t[i] = i end
  352. return table.unpack(t)
  353. end)
  354. local r, msg = coroutine.resume(co)
  355. assert(not r)
  356. end
  357. end
  358. assert(coroutine.running() == main)
  359. print"+"
  360. print"testing yields inside metamethods"
  361. local mt = {
  362. __eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end,
  363. __lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end,
  364. __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
  365. __add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end,
  366. __sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end,
  367. __concat = function(a,b)
  368. coroutine.yield(nil, "concat");
  369. a = type(a) == "table" and a.x or a
  370. b = type(b) == "table" and b.x or b
  371. return a .. b
  372. end,
  373. __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
  374. __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
  375. }
  376. local function new (x)
  377. return setmetatable({x = x, k = {}}, mt)
  378. end
  379. local a = new(10)
  380. local b = new(12)
  381. local c = new"hello"
  382. local function run (f, t)
  383. local i = 1
  384. local c = coroutine.wrap(f)
  385. while true do
  386. local res, stat = c()
  387. if res then assert(t[i] == nil); return res, t end
  388. assert(stat == t[i])
  389. i = i + 1
  390. end
  391. end
  392. assert(run(function () if (a>=b) then return '>=' else return '<' end end,
  393. {"le", "sub"}) == "<")
  394. -- '<=' using '<'
  395. mt.__le = nil
  396. assert(run(function () if (a<=b) then return '<=' else return '>' end end,
  397. {"lt"}) == "<=")
  398. assert(run(function () if (a==b) then return '==' else return '~=' end end,
  399. {"eq"}) == "~=")
  400. assert(run(function () return a..b end, {"concat"}) == "1012")
  401. assert(run(function() return a .. b .. c .. a end,
  402. {"concat", "concat", "concat"}) == "1012hello10")
  403. assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
  404. {"concat", "concat", "concat"}) == "ab10chello12x")
  405. assert(run(function ()
  406. a.BB = print
  407. return a.BB
  408. end, {"nidx", "idx"}) == print)
  409. -- getuptable & setuptable
  410. do local _ENV = _ENV
  411. f = function () AAA = BBB + 1; return AAA end
  412. end
  413. g = new(10); g.k.BBB = 10;
  414. debug.setupvalue(f, 1, g)
  415. assert(run(f, {"idx", "nidx", "idx"}) == 11)
  416. assert(g.k.AAA == 11)
  417. print"+"
  418. print"testing yields inside 'for' iterators"
  419. local f = function (s, i)
  420. if i%2 == 0 then coroutine.yield(nil, "for") end
  421. if i < s then return i + 1 end
  422. end
  423. assert(run(function ()
  424. local s = 0
  425. for i in f, 4, 0 do s = s + i end
  426. return s
  427. end, {"for", "for", "for"}) == 10)
  428. -- tests for coroutine API
  429. if T==nil then
  430. (Message or print)('\a\n >>> testC not active: skipping coroutine API tests <<<\n\a')
  431. return
  432. end
  433. print('testing coroutine API')
  434. local function apico (...)
  435. local x = {...}
  436. return coroutine.wrap(function ()
  437. return T.testC(table.unpack(x))
  438. end)
  439. end
  440. local a = {apico(
  441. [[
  442. pushstring errorcode
  443. pcallk 1 0 2;
  444. invalid command (should not arrive here)
  445. ]],
  446. [[getctx; gettop; return .]],
  447. "stackmark",
  448. error
  449. )()}
  450. assert(#a == 6 and
  451. a[3] == "stackmark" and
  452. a[4] == "errorcode" and
  453. a[5] == "ERRRUN" and
  454. a[6] == 2) -- 'ctx' to pcallk
  455. local co = apico(
  456. "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
  457. coroutine.yield,
  458. "getctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
  459. "getctx; gettop; return .")
  460. assert(co() == 10)
  461. assert(co(20, 30) == 'a')
  462. a = {co()}
  463. assert(#a == 10 and
  464. a[2] == coroutine.yield and
  465. a[5] == 20 and a[6] == 30 and
  466. a[7] == "YIELD" and a[8] == 3 and
  467. a[9] == "YIELD" and a[10] == 4)
  468. assert(not pcall(co)) -- coroutine is dead now
  469. f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
  470. co = coroutine.wrap(function ()
  471. assert(f() == 23); assert(f() == 23); return 10
  472. end)
  473. assert(co(23,16) == 5)
  474. assert(co(23,16) == 5)
  475. assert(co(23,16) == 10)
  476. -- testing coroutines with C bodies
  477. f = T.makeCfunc([[
  478. pushnum 102
  479. yieldk 1 U2
  480. return 2
  481. ]],
  482. [[
  483. pushnum 23 # continuation
  484. gettop
  485. return .
  486. ]])
  487. x = coroutine.wrap(f)
  488. assert(x() == 102)
  489. assert(x() == 23)
  490. f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
  491. a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
  492. pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
  493. return 4; ]], f)
  494. assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
  495. -- testing chain of suspendable C calls
  496. local count = 3 -- number of levels
  497. f = T.makeCfunc([[
  498. remove 1; # remove argument
  499. pushvalue U3; # get selection function
  500. call 0 1; # call it (result is 'f' or 'yield')
  501. pushstring hello # single argument for selected function
  502. pushupvalueindex 2; # index of continuation program
  503. callk 1 -1 .; # call selected function
  504. errorerror # should never arrive here
  505. ]],
  506. [[
  507. # continuation program
  508. pushnum 34 # return value
  509. gettop
  510. return . # return all results
  511. ]],
  512. function () -- selection function
  513. count = count - 1
  514. if count == 0 then return coroutine.yield
  515. else return f
  516. end
  517. end
  518. )
  519. co = coroutine.wrap(function () return f(nil) end)
  520. assert(co() == "hello") -- argument to 'yield'
  521. a = {co()}
  522. -- three '34's (one from each pending C call)
  523. assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
  524. -- testing yields with continuations
  525. co = coroutine.wrap(function (...) return
  526. T.testC([[
  527. getctx
  528. yieldk 3 2
  529. nonexec error
  530. ]],
  531. [[ # continuation
  532. getctx
  533. yieldk 2 3
  534. ]],
  535. [[ # continuation
  536. getctx
  537. yieldk 2 4
  538. ]],
  539. [[ # continuation
  540. pushvalue 6; pushnum 10; pushnum 20;
  541. pcall 2 0 # call should throw an error and execution continues
  542. pop 1 # remove error message
  543. pushvalue 6
  544. getctx
  545. pcallk 2 2 5 # call should throw an error and jump to continuation
  546. cannot be here!
  547. ]],
  548. [[ # continuation
  549. gettop
  550. return .
  551. ]],
  552. function (a,b) x=a; y=b; error("errmsg") end,
  553. ...
  554. )
  555. end)
  556. local a = {co(3,4,6)}; assert(a[1] == 6 and a[2] == "OK" and a[3] == 0)
  557. a = {co()}; assert(a[1] == "YIELD" and a[2] == 2)
  558. a = {co()}; assert(a[1] == "YIELD" and a[2] == 3)
  559. a = {co(7,8)};
  560. -- original arguments
  561. assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
  562. type(a[3]) == 'string' and type(a[4]) == 'string' and
  563. type(a[5]) == 'string' and type(a[6]) == 'function')
  564. -- arguments left from fist resume
  565. assert(a[7] == 3 and a[8] == 4)
  566. -- arguments to last resume
  567. assert(a[9] == 7 and a[10] == 8)
  568. -- error message and nothing more
  569. assert(a[11]:find("errmsg") and #a == 11)
  570. -- check arguments to pcallk
  571. assert(x == "YIELD" and y == 4)
  572. assert(not pcall(co)) -- coroutine should be dead
  573. -- testing ctx
  574. a,b = T.testC(
  575. [[ pushstring print; pcallk 0 0 12 # error
  576. getctx; return 2 ]])
  577. assert(a == "OK" and b == 0) -- no ctx outside continuations
  578. -- bug in nCcalls
  579. local co = coroutine.wrap(function ()
  580. local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
  581. return pcall(assert, table.unpack(a))
  582. end)
  583. local a = {co()}
  584. assert(a[10] == "hi")
  585. print'OK'