| # vim:set ft= ts=4 sw=4 et fdm=marker: |
| |
| use lib 'lib'; |
| use Test::Nginx::Socket::Lua; |
| |
| repeat_each(2); |
| |
| plan tests => repeat_each() * (blocks() * 3 + 3); |
| |
| $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; |
| |
| our $StapScript = <<'_EOC_'; |
| global ids, cur |
| |
| function gen_id(k) { |
| if (ids[k]) return ids[k] |
| ids[k] = ++cur |
| return cur |
| } |
| |
| F(ngx_http_handler) { |
| delete ids |
| cur = 0 |
| } |
| |
| /* |
| F(ngx_http_lua_run_thread) { |
| id = gen_id($ctx->cur_co) |
| printf("run thread %d\n", id) |
| } |
| |
| probe process("/usr/local/openresty-debug/luajit/lib/libluajit-5.1.so.2").function("lua_resume") { |
| id = gen_id($L) |
| printf("lua resume %d\n", id) |
| } |
| */ |
| |
| M(http-lua-user-coroutine-resume) { |
| p = gen_id($arg2) |
| c = gen_id($arg3) |
| printf("resume %x in %x\n", c, p) |
| } |
| |
| M(http-lua-thread-yield) { |
| println("thread yield") |
| } |
| |
| /* |
| F(ngx_http_lua_coroutine_yield) { |
| printf("yield %x\n", gen_id($L)) |
| } |
| */ |
| |
| M(http-lua-user-coroutine-yield) { |
| p = gen_id($arg2) |
| c = gen_id($arg3) |
| printf("yield %x in %x\n", c, p) |
| } |
| |
| F(ngx_http_lua_atpanic) { |
| printf("lua atpanic(%d):", gen_id($L)) |
| print_ubacktrace(); |
| } |
| |
| M(http-lua-user-coroutine-create) { |
| p = gen_id($arg2) |
| c = gen_id($arg3) |
| printf("create %x in %x\n", c, p) |
| } |
| |
| F(ngx_http_lua_ngx_exec) { println("exec") } |
| |
| F(ngx_http_lua_ngx_exit) { println("exit") } |
| F(ngx_http_lua_ffi_exit) { println("exit") } |
| _EOC_ |
| |
| no_shuffle(); |
| no_long_string(); |
| run_tests(); |
| |
| __DATA__ |
| |
| === TEST 1: basic coroutine print |
| --- config |
| location /lua { |
| content_by_lua ' |
| local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield |
| |
| function f() |
| local cnt = 0 |
| for i = 1, 20 do |
| ngx.say("Hello, ", cnt) |
| cy() |
| cnt = cnt + 1 |
| end |
| end |
| |
| local c = cc(f) |
| for i=1,3 do |
| cr(c) |
| ngx.say("***") |
| end |
| '; |
| } |
| --- request |
| GET /lua |
| --- stap2 eval: $::StapScript |
| --- response_body |
| Hello, 0 |
| *** |
| Hello, 1 |
| *** |
| Hello, 2 |
| *** |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 2: basic coroutine2 |
| --- config |
| location /lua { |
| content_by_lua ' |
| function f(fid) |
| local cnt = 0 |
| while true do |
| ngx.say("cc", fid, ": ", cnt) |
| coroutine.yield() |
| cnt = cnt + 1 |
| end |
| end |
| |
| local ccs = {} |
| for i=1,3 do |
| ccs[#ccs+1] = coroutine.create(function() f(i) end) |
| end |
| |
| for i=1,9 do |
| local cc = table.remove(ccs, 1) |
| coroutine.resume(cc) |
| ccs[#ccs+1] = cc |
| end |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| cc1: 0 |
| cc2: 0 |
| cc3: 0 |
| cc1: 1 |
| cc2: 1 |
| cc3: 1 |
| cc1: 2 |
| cc2: 2 |
| cc3: 2 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 3: basic coroutine and cosocket |
| --- config |
| resolver $TEST_NGINX_RESOLVER; |
| location /lua { |
| content_by_lua ' |
| function worker(url) |
| local sock = ngx.socket.tcp() |
| local ok, err = sock:connect(url, 80) |
| coroutine.yield() |
| if not ok then |
| ngx.say("failed to connect to: ", url, " error: ", err) |
| return |
| end |
| coroutine.yield() |
| ngx.say("successfully connected to: ", url) |
| sock:close() |
| end |
| |
| local urls = { |
| "agentzh.org", |
| "agentzh.com", |
| "openresty.org" |
| } |
| |
| local ccs = {} |
| for i, url in ipairs(urls) do |
| local cc = coroutine.create(function() worker(url) end) |
| ccs[#ccs+1] = cc |
| end |
| |
| while true do |
| if #ccs == 0 then break end |
| local cc = table.remove(ccs, 1) |
| local ok = coroutine.resume(cc) |
| if ok then |
| ccs[#ccs+1] = cc |
| end |
| end |
| |
| ngx.say("*** All Done ***") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| successfully connected to: agentzh.org |
| successfully connected to: agentzh.com |
| successfully connected to: openresty.org |
| *** All Done *** |
| --- no_error_log |
| [error] |
| --- timeout: 10 |
| |
| |
| |
| === TEST 4: coroutine.wrap(generate prime numbers) |
| --- config |
| location /lua { |
| content_by_lua ' |
| -- generate all the numbers from 2 to n |
| function gen (n) |
| return coroutine.wrap(function () |
| for i=2,n do coroutine.yield(i) end |
| end) |
| end |
| |
| -- filter the numbers generated by g, removing multiples of p |
| function filter (p, g) |
| return coroutine.wrap(function () |
| while 1 do |
| local n = g() |
| if n == nil then return end |
| if math.fmod(n, p) ~= 0 then coroutine.yield(n) end |
| end |
| end) |
| end |
| |
| N = 10 |
| x = gen(N) -- generate primes up to N |
| while 1 do |
| local n = x() -- pick a number until done |
| if n == nil then break end |
| ngx.say(n) -- must be a prime number |
| x = filter(n, x) -- now remove its multiples |
| end |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| 2 |
| 3 |
| 5 |
| 7 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 5: coroutine.wrap(generate prime numbers,reset create and resume) |
| --- config |
| location /lua { |
| content_by_lua ' |
| coroutine.create = nil |
| coroutine.resume = nil |
| -- generate all the numbers from 2 to n |
| function gen (n) |
| return coroutine.wrap(function () |
| for i=2,n do coroutine.yield(i) end |
| end) |
| end |
| |
| -- filter the numbers generated by g, removing multiples of p |
| function filter (p, g) |
| return coroutine.wrap(function () |
| while 1 do |
| local n = g() |
| if n == nil then return end |
| if math.fmod(n, p) ~= 0 then coroutine.yield(n) end |
| end |
| end) |
| end |
| |
| N = 10 |
| x = gen(N) -- generate primes up to N |
| while 1 do |
| local n = x() -- pick a number until done |
| if n == nil then break end |
| ngx.say(n) -- must be a prime number |
| x = filter(n, x) -- now remove its multiples |
| end |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| 2 |
| 3 |
| 5 |
| 7 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 6: coroutine.wrap(generate fib) |
| --- config |
| location /lua { |
| content_by_lua ' |
| function generatefib (n) |
| return coroutine.wrap(function () |
| local a,b = 1, 1 |
| while a <= n do |
| coroutine.yield(a) |
| a, b = b, a+b |
| end |
| end) |
| end |
| |
| -- In lua, because OP_TFORLOOP uses luaD_call to execute the iterator function, |
| -- and luaD_call is a C function, so we can not yield in the iterator function. |
| -- So the following case(using for loop) will be failed. |
| -- Luajit is OK. |
| if package.loaded["jit"] then |
| for i in generatefib(1000) do ngx.say(i) end |
| else |
| local gen = generatefib(1000) |
| while true do |
| local i = gen() |
| if not i then break end |
| ngx.say(i) |
| end |
| end |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| 1 |
| 1 |
| 2 |
| 3 |
| 5 |
| 8 |
| 13 |
| 21 |
| 34 |
| 55 |
| 89 |
| 144 |
| 233 |
| 377 |
| 610 |
| 987 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 7: coroutine wrap and cosocket |
| --- config |
| resolver $TEST_NGINX_RESOLVER; |
| location /lua { |
| content_by_lua ' |
| function worker(url) |
| local sock = ngx.socket.tcp() |
| local ok, err = sock:connect(url, 80) |
| coroutine.yield() |
| if not ok then |
| ngx.say("failed to connect to: ", url, " error: ", err) |
| return |
| end |
| coroutine.yield() |
| ngx.say("successfully connected to: ", url) |
| sock:close() |
| end |
| |
| local urls = { |
| "agentzh.org", |
| "agentzh.com", |
| "openresty.org" |
| } |
| |
| local cfs = {} |
| for i, url in ipairs(urls) do |
| local cf = coroutine.wrap(function() worker(url) end) |
| cfs[#cfs+1] = cf |
| end |
| |
| for i=1,3 do cfs[i]() end |
| for i=1,3 do cfs[i]() end |
| for i=1,3 do cfs[i]() end |
| |
| ngx.say("*** All Done ***") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| successfully connected to: agentzh.org |
| successfully connected to: agentzh.com |
| successfully connected to: openresty.org |
| *** All Done *** |
| --- no_error_log |
| [error] |
| --- timeout: 10 |
| |
| |
| |
| === TEST 8: coroutine status, running |
| --- config |
| location /lua { |
| content_by_lua ' |
| local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield |
| local st, rn = coroutine.status, coroutine.running |
| |
| function f(self) |
| local cnt = 0 |
| if rn() ~= self then ngx.say("error"); return end |
| ngx.say("running: ", st(self)) --running |
| cy() |
| local c = cc(function(father) |
| ngx.say("normal: ", st(father)) |
| end) -- normal |
| cr(c, self) |
| end |
| |
| local c = cc(f) |
| ngx.say("suspended: ", st(c)) -- suspended |
| cr(c, c) |
| ngx.say("suspended: ", st(c)) -- suspended |
| cr(c, c) |
| ngx.say("dead: ", st(c)) -- dead |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| suspended: suspended |
| running: running |
| suspended: suspended |
| normal: normal |
| dead: dead |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 9: entry coroutine yielded will be resumed immediately |
| --- config |
| location /lua { |
| content_by_lua ' |
| ngx.say("[", {coroutine.yield()}, "]") |
| ngx.say("[", {coroutine.yield(1, "a")}, "]") |
| ngx.say("done") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| [] |
| [] |
| done |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 10: thread traceback (multi-thread) |
| --- config |
| location /lua { |
| content_by_lua ' |
| local f = function(cr) coroutine.resume(cr) end |
| -- emit a error |
| local g = function() unknown.unknown = 1 end |
| local l1 = coroutine.create(f) |
| local l2 = coroutine.create(g) |
| coroutine.resume(l1, l2) |
| ngx.say("hello") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| hello |
| --- error_log eval |
| ["stack traceback:", "coroutine 0:", "coroutine 1:", "coroutine 2:"] |
| |
| |
| |
| === TEST 11: thread traceback (only the entry thread) |
| --- config |
| location /lua { |
| content_by_lua ' |
| -- emit a error |
| unknown.unknown = 1 |
| ngx.say("hello") |
| '; |
| } |
| --- request |
| GET /lua |
| --- error_code: 500 |
| --- error_log eval |
| ["stack traceback:", "coroutine 0:"] |
| |
| |
| |
| === TEST 12: bug: resume dead coroutine with args |
| --- config |
| location /lua { |
| content_by_lua ' |
| function print(...) |
| local args = {...} |
| local is_first = true |
| for i,v in ipairs(args) do |
| if is_first then |
| is_first = false |
| else |
| ngx.print(" ") |
| end |
| ngx.print(v) |
| end |
| ngx.print("\\\n") |
| end |
| |
| function foo (a) |
| print("foo", a) |
| return coroutine.yield(2*a) |
| end |
| |
| co = coroutine.create(function (a,b) |
| print("co-body", a, b) |
| local r = foo(a+1) |
| print("co-body", r) |
| local r, s = coroutine.yield(a+b, a-b) |
| print("co-body", r, s) |
| return b, "end" |
| end) |
| |
| print("main", coroutine.resume(co, 1, 10)) |
| print("main", coroutine.resume(co, "r")) |
| print("main", coroutine.resume(co, "x", "y")) |
| print("main", coroutine.resume(co, "x", "y")) |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| co-body 1 10 |
| foo 2 |
| main true 4 |
| co-body r |
| main true 11 -9 |
| co-body x y |
| main true 10 end |
| main false cannot resume dead coroutine |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 13: deeply nested coroutines |
| --- config |
| location /lua { |
| content_by_lua ' |
| local create = coroutine.create |
| local resume = coroutine.resume |
| local yield = coroutine.yield |
| function f() |
| ngx.say("f begin") |
| yield() |
| local c2 = create(g) |
| ngx.say("1: resuming c2") |
| resume(c2) |
| ngx.say("2: resuming c2") |
| resume(c2) |
| yield() |
| ngx.say("3: resuming c2") |
| resume(c2) |
| ngx.say("f done") |
| end |
| |
| function g() |
| ngx.say("g begin") |
| yield() |
| ngx.say("g going") |
| yield() |
| ngx.say("g done") |
| end |
| |
| local c1 = create(f) |
| ngx.say("1: resuming c1") |
| resume(c1) |
| ngx.say("2: resuming c1") |
| resume(c1) |
| ngx.say("3: resuming c1") |
| resume(c1) |
| ngx.say("main done") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| 1: resuming c1 |
| f begin |
| 2: resuming c1 |
| 1: resuming c2 |
| g begin |
| 2: resuming c2 |
| g going |
| 3: resuming c1 |
| 3: resuming c2 |
| g done |
| f done |
| main done |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 14: using ngx.exit in user coroutines |
| --- config |
| location /lua { |
| content_by_lua ' |
| local create = coroutine.create |
| local resume = coroutine.resume |
| local yield = coroutine.yield |
| |
| local code = 400 |
| |
| function f() |
| local c2 = create(g) |
| yield() |
| code = code + 1 |
| resume(c2) |
| yield() |
| resume(c2) |
| end |
| |
| function g() |
| code = code + 1 |
| yield() |
| code = code + 1 |
| ngx.exit(code) |
| end |
| |
| local c1 = create(f) |
| resume(c1) |
| resume(c1) |
| resume(c1) |
| ngx.say("done") |
| '; |
| } |
| --- request |
| GET /lua |
| --- stap eval: $::StapScript |
| --- stap_out |
| create 2 in 1 |
| resume 2 in 1 |
| create 3 in 2 |
| yield 2 in 1 |
| resume 2 in 1 |
| resume 3 in 2 |
| yield 3 in 2 |
| yield 2 in 1 |
| resume 2 in 1 |
| resume 3 in 2 |
| exit |
| |
| --- response_body_like: 403 Forbidden |
| --- error_code: 403 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 15: using ngx.exec in user coroutines |
| --- config |
| location /lua { |
| content_by_lua ' |
| local create = coroutine.create |
| local resume = coroutine.resume |
| local yield = coroutine.yield |
| |
| local code = 0 |
| |
| function f() |
| local c2 = create(g) |
| yield() |
| code = code + 1 |
| resume(c2) |
| yield() |
| resume(c2) |
| end |
| |
| function g() |
| code = code + 1 |
| yield() |
| code = code + 1 |
| ngx.exec("/n/" .. code) |
| end |
| |
| local c1 = create(f) |
| resume(c1) |
| resume(c1) |
| resume(c1) |
| ngx.say("done") |
| '; |
| } |
| |
| location ~ '^/n/(\d+)' { |
| echo "num: $1"; |
| } |
| |
| --- stap eval: $::StapScript |
| --- stap_out |
| create 2 in 1 |
| resume 2 in 1 |
| create 3 in 2 |
| yield 2 in 1 |
| resume 2 in 1 |
| resume 3 in 2 |
| yield 3 in 2 |
| yield 2 in 1 |
| resume 2 in 1 |
| resume 3 in 2 |
| exec |
| |
| --- request |
| GET /lua |
| --- response_body |
| num: 3 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 16: coroutine.create in header_filter_by_lua |
| --- config |
| location /lua { |
| echo hello; |
| header_filter_by_lua ' |
| function f() |
| yield() |
| end |
| |
| local c1 = coroutine.create(f) |
| ngx.say("done") |
| '; |
| } |
| --- request |
| GET /lua |
| --- ignore_response |
| --- error_log |
| API disabled in the context of header_filter_by_lua* |
| |
| |
| |
| === TEST 17: resume coroutines from within another one that is not its parent |
| --- config |
| location /t { |
| content_by_lua ' |
| local print = ngx.say |
| |
| local c1, c2 |
| |
| function f() |
| print("f 1") |
| print(coroutine.resume(c2)) |
| print("f 2") |
| end |
| |
| function g() |
| print("g 1") |
| -- print(coroutine.resume(c1)) |
| print("g 2") |
| end |
| |
| c1 = coroutine.create(f) |
| c2 = coroutine.create(g) |
| |
| coroutine.resume(c1) |
| '; |
| } |
| --- request |
| GET /t |
| --- response_body |
| f 1 |
| g 1 |
| g 2 |
| true |
| f 2 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 18: infinite recursive calls of coroutine.resume |
| --- config |
| location /t { |
| content_by_lua ' |
| local print = ngx.say |
| |
| local c1, c2 |
| |
| function f() |
| print("f 1") |
| print(coroutine.resume(c2)) |
| print("f 2") |
| end |
| |
| function g() |
| print("g 1") |
| print(coroutine.resume(c1)) |
| print("g 2") |
| end |
| |
| c1 = coroutine.create(f) |
| c2 = coroutine.create(g) |
| |
| coroutine.resume(c1) |
| '; |
| } |
| --- request |
| GET /t |
| --- stap2 eval: $::StapScript |
| --- response_body |
| f 1 |
| g 1 |
| falsecannot resume normal coroutine |
| g 2 |
| true |
| f 2 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 19: resume running (entry) coroutines |
| --- config |
| location /t { |
| content_by_lua ' |
| ngx.say(coroutine.status(coroutine.running())) |
| ngx.say(coroutine.resume(coroutine.running())) |
| '; |
| } |
| --- request |
| GET /t |
| --- response_body |
| running |
| falsecannot resume running coroutine |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 20: resume running (user) coroutines |
| --- config |
| location /t { |
| content_by_lua ' |
| local co |
| function f() |
| ngx.say("f: ", coroutine.status(co)) |
| ngx.say("f: ", coroutine.resume(co)) |
| end |
| co = coroutine.create(f) |
| ngx.say("chunk: ", coroutine.status(co)) |
| ngx.say("chunk: ", coroutine.resume(co)) |
| '; |
| } |
| --- request |
| GET /t |
| --- response_body |
| chunk: suspended |
| f: running |
| f: falsecannot resume running coroutine |
| chunk: true |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 21: user coroutine end with errors, and the parent coroutine gets the right status |
| --- config |
| location /t { |
| content_by_lua ' |
| local co |
| function f() |
| error("bad") |
| end |
| co = coroutine.create(f) |
| ngx.say("child: resume: ", coroutine.resume(co)) |
| ngx.say("child: status: ", coroutine.status(co)) |
| ngx.say("parent: status: ", coroutine.status(coroutine.running())) |
| '; |
| } |
| --- request |
| GET /t |
| --- response_body |
| child: resume: false[string "content_by_lua"]:4: bad |
| child: status: dead |
| parent: status: running |
| --- error_log |
| lua coroutine: runtime error: [string "content_by_lua"]:4: bad |
| |
| |
| |
| === TEST 22: entry coroutine is yielded by hand and still gets the right status |
| --- config |
| location /t { |
| content_by_lua ' |
| local co = coroutine.running() |
| ngx.say("status: ", coroutine.status(co)) |
| coroutine.yield(co) |
| ngx.say("status: ", coroutine.status(co)) |
| '; |
| } |
| --- request |
| GET /t |
| --- response_body |
| status: running |
| status: running |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 23: github issue #208: coroutine as iterator doesn't work |
| --- config |
| location = /t { |
| content_by_lua ' |
| local say = ngx.say |
| local wrap, yield = coroutine.wrap, coroutine.yield |
| |
| local function it(it_state) |
| for i = 1, it_state.i do |
| yield(it_state.path, tostring(i)) |
| end |
| return nil |
| end |
| |
| local function it_factory(path) |
| local it_state = { i = 10, path = path } |
| return wrap(it), it_state |
| end |
| |
| --[[ |
| for path, value in it_factory("test") do |
| say(path, value) |
| end |
| ]] |
| |
| do |
| local f, s, var = it_factory("test") |
| while true do |
| local path, value = f(s, var) |
| var = path |
| if var == nil then break end |
| say(path, value) |
| end |
| end |
| '; |
| } |
| --- request |
| GET /t |
| --- more_headers |
| Cookie: abc=32 |
| --- stap2 eval: $::StapScript |
| --- response_body |
| test1 |
| test2 |
| test3 |
| test4 |
| test5 |
| test6 |
| test7 |
| test8 |
| test9 |
| test10 |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 24: init_by_lua + our own coroutines in content_by_lua |
| --- http_config |
| init_by_lua 'return'; |
| --- config |
| resolver $TEST_NGINX_RESOLVER; |
| location /lua { |
| content_by_lua ' |
| function worker(url) |
| local sock = ngx.socket.tcp() |
| local ok, err = sock:connect(url, 80) |
| coroutine.yield() |
| if not ok then |
| ngx.say("failed to connect to: ", url, " error: ", err) |
| return |
| end |
| coroutine.yield() |
| ngx.say("successfully connected to: ", url) |
| sock:close() |
| end |
| |
| local urls = { |
| "agentzh.org", |
| } |
| |
| local ccs = {} |
| for i, url in ipairs(urls) do |
| local cc = coroutine.create(function() worker(url) end) |
| ccs[#ccs+1] = cc |
| end |
| |
| while true do |
| if #ccs == 0 then break end |
| local cc = table.remove(ccs, 1) |
| local ok = coroutine.resume(cc) |
| if ok then |
| ccs[#ccs+1] = cc |
| end |
| end |
| |
| ngx.say("*** All Done ***") |
| '; |
| } |
| --- request |
| GET /lua |
| --- response_body |
| successfully connected to: agentzh.org |
| *** All Done *** |
| --- no_error_log |
| [error] |
| --- timeout: 10 |
| |
| |
| |
| === TEST 25: init_by_lua_file + our own coroutines in content_by_lua |
| --- http_config |
| init_by_lua_file html/init.lua; |
| |
| --- config |
| resolver $TEST_NGINX_RESOLVER; |
| location /lua { |
| content_by_lua ' |
| function worker(url) |
| local sock = ngx.socket.tcp() |
| local ok, err = sock:connect(url, 80) |
| coroutine.yield() |
| if not ok then |
| ngx.say("failed to connect to: ", url, " error: ", err) |
| return |
| end |
| coroutine.yield() |
| ngx.say("successfully connected to: ", url) |
| sock:close() |
| end |
| |
| local urls = { |
| "agentzh.org" |
| } |
| |
| local ccs = {} |
| for i, url in ipairs(urls) do |
| local cc = coroutine.create(function() worker(url) end) |
| ccs[#ccs+1] = cc |
| end |
| |
| while true do |
| if #ccs == 0 then break end |
| local cc = table.remove(ccs, 1) |
| local ok = coroutine.resume(cc) |
| if ok then |
| ccs[#ccs+1] = cc |
| end |
| end |
| |
| ngx.say("*** All Done ***") |
| '; |
| } |
| --- user_files |
| >>> init.lua |
| return |
| |
| --- request |
| GET /lua |
| --- response_body |
| successfully connected to: agentzh.org |
| *** All Done *** |
| --- no_error_log |
| [error] |
| --- timeout: 10 |
| |
| |
| |
| === TEST 26: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua |
| --- http_config |
| init_by_lua ' |
| co_wrap = coroutine.wrap |
| co_yield = coroutine.yield |
| '; |
| |
| --- config |
| location /cotest { |
| content_by_lua ' |
| function generator() |
| return co_wrap(function() |
| co_yield("data") |
| end) |
| end |
| |
| local co = generator() |
| local data = co() |
| ngx.say(data) |
| '; |
| } |
| |
| --- request |
| GET /cotest |
| --- stap2 eval: $::StapScript |
| --- response_body |
| data |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 27: mixing coroutine.* API between init_by_lua and other contexts (github #304) - init_by_lua_file |
| --- http_config |
| init_by_lua_file html/init.lua; |
| |
| --- config |
| location /cotest { |
| content_by_lua ' |
| function generator() |
| return co_wrap(function() |
| co_yield("data") |
| end) |
| end |
| |
| local co = generator() |
| local data = co() |
| ngx.say(data) |
| '; |
| } |
| |
| --- user_files |
| >>> init.lua |
| co_wrap = coroutine.wrap |
| co_yield = coroutine.yield |
| |
| --- request |
| GET /cotest |
| --- stap2 eval: $::StapScript |
| --- response_body |
| data |
| --- no_error_log |
| [error] |
| |
| |
| |
| === TEST 28: coroutine context collicisions |
| --- config |
| location /lua { |
| content_by_lua ' |
| local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield |
| |
| function f() |
| return 3 |
| end |
| |
| for i = 1, 10 do |
| collectgarbage() |
| local c = cc(f) |
| if coroutine.status(c) == "dead" then |
| ngx.say("found a dead coroutine") |
| return |
| end |
| cr(c) |
| end |
| ngx.say("ok") |
| '; |
| } |
| --- request |
| GET /lua |
| --- stap2 eval: $::StapScript |
| --- response_body |
| ok |
| --- no_error_log |
| [error] |
| |