# vim:set ft=ts=4 sw=4 et fdm=marker: use lib 'lib'; use Test::Nginx::Socket::Lua; use Cwd qw(cwd); #worker_connections(10140); #workers(1); #log_level('warn'); repeat_each(2); plan tests => repeat_each() * (blocks() * 3 + 3); my $pwd = cwd(); no_long_string(); #no_diff(); $ENV{TEST_NGINX_LUA_PACKAGE_PATH} = "\"$pwd/lib/?.lua;;\""; our $HttpConfig = <<_EOC_; lua_package_path "$pwd/lib/?.lua;;"; _EOC_ run_tests(); __DATA__ === TEST 1: basic semaphore in uthread --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new() local function sem_wait() ngx.say("enter waiting") local ok, err = sem:wait(1) if not ok then ngx.say("err: ", err) else ngx.say("wait success") end end local co = ngx.thread.spawn(sem_wait) ngx.say("back in main thread") sem:post() ngx.say("still in main thread") ngx.sleep(0.01) ngx.say("main thread end") } } --- request GET /test --- response_body enter waiting back in main thread still in main thread wait success main thread end --- no_error_log [error] === TEST 2: semaphore wait order --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new() local function sem_wait(id) ngx.say("enter waiting, id: ", id) local ok, err = sem:wait(1) if not ok then ngx.say("err: ", err) else ngx.say("wait success, id: ", id) end end local co1 = ngx.thread.spawn(sem_wait, 1) local co2 = ngx.thread.spawn(sem_wait, 2) ngx.say("back in main thread") sem:post(2) local ok, err = sem:wait(0) if ok then ngx.say("wait success in main thread") else ngx.say("wait failed in main thread: ", err) -- busy end ngx.say("still in main thread") local ok, err = sem:wait(0.01) if ok then ngx.say("wait success in main thread") else ngx.say("wait failed in main thread: ", err) end ngx.sleep(0.01) ngx.say("main thread end") } } --- request GET /test --- response_body enter waiting, id: 1 enter waiting, id: 2 back in main thread wait failed in main thread: timeout still in main thread wait success, id: 1 wait success, id: 2 wait failed in main thread: timeout main thread end --- no_error_log [error] === TEST 3: semaphore wait time=0 --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(1) local function wait_1s() ngx.say("enter 1s wait") local ok, err = sem:wait(1) if not ok then ngx.say("err in wait 1s: ", err) else ngx.say("wait success in 1s wait") end end local function wait_0() local ok, err = sem:wait(0) if not ok then ngx.say("err: ", err) else ngx.say("wait success") end end wait_0() wait_0() local co = ngx.thread.spawn(wait_1s) ngx.say("back in main thread") wait_0() sem:post(2) wait_0() ngx.say("still in main thread") ngx.sleep(0.01) wait_0() ngx.say("main thread end") } } --- request GET /test --- response_body wait success err: timeout enter 1s wait back in main thread err: timeout err: timeout still in main thread wait success in 1s wait wait success main thread end --- no_error_log [error] === TEST 4: basic semaphore in subrequest --- http_config eval: $::HttpConfig --- config location = /test { content_by_lua_block { local res1, res2 = ngx.location.capture_multi{ { "/sem_wait"}, { "/sem_post"}, } ngx.say(res1.status) ngx.say(res1.body) ngx.say(res2.status) ngx.say(res2.body) } } location /sem_wait { content_by_lua_block { local semaphore = require "ngx.semaphore" local g = package.loaded["semaphore_test"] or {} package.loaded["semaphore_test"] = g if not g.test then local sem, err = semaphore.new(0) if not sem then ngx.say(err) return end g.test = sem end local sem = g.test local ok, err = sem:wait(1) if ok then ngx.print("wait") end } } location /sem_post { content_by_lua_block { local semaphore = require "ngx.semaphore" local g = package.loaded["semaphore_test"] or {} package.loaded["semaphore_test"] = g if not g.test then local sem, err = semaphore.new(0) if not sem then ngx.say(err) ngx.exit(500) end g.test = sem end local sem = g.test ngx.sleep(0.001) collectgarbage("collect") sem:post() ngx.print("post") } } --- request GET /test --- response_body 200 wait 200 post --- no_error_log [error] [crit] === TEST 5: semaphore.new in init_by_lua* (w/o shdict) --- http_config lua_package_path $TEST_NGINX_LUA_PACKAGE_PATH; init_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) else ngx.log(ngx.WARN, "sema created: ", tostring(sem)) end sem:post(2) package.loaded.my_sema = sem } --- config location /test { content_by_lua_block { local sem = package.loaded.my_sema ngx.say("sem count: ", sem:count()) -- sem:post(1) local ok, err = sem:wait(0) if not ok then ngx.say("failed to wait: ", err) return end ngx.say("waited successfully.") } } --- request GET /test --- response_body_like sem count: [12] waited successfully. --- grep_error_log eval qr/\[lua\] init_by_lua:\d+: sema created: table: 0x[a-f0-9]+/ --- grep_error_log_out eval [ qr/\[lua\] init_by_lua:\d+: sema created: table: 0x[a-f0-9]+/, "", ] === TEST 6: semaphore.new in init_by_lua* (with shdict) --- http_config lua_shared_dict dogs 1m; lua_package_path $TEST_NGINX_LUA_PACKAGE_PATH; init_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) else ngx.log(ngx.WARN, "sema created: ", tostring(sem)) end sem:post(2) package.loaded.my_sema = sem } --- config location /test { content_by_lua_block { local sem = package.loaded.my_sema ngx.say("sem count: ", sem:count()) -- sem:post(1) local ok, err = sem:wait(0) if not ok then ngx.say("failed to wait: ", err) return end ngx.say("waited successfully.") } } --- request GET /test --- response_body_like sem count: [12] waited successfully. --- grep_error_log eval qr/\[lua\] init_by_lua:\d+: sema created: table: 0x[a-f0-9]+/ --- grep_error_log_out eval [ qr/\[lua\] init_by_lua:\d+: sema created: table: 0x[a-f0-9]+/, "", ] === TEST 7: semaphore in init_worker_by_lua (wait is not allowed) --- http_config lua_package_path $TEST_NGINX_LUA_PACKAGE_PATH; init_worker_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem new: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem count: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem wait: ", err) end } --- config location /t { echo "ok"; } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem \w+: .*?,/ --- grep_error_log_out eval [ "sem count: 1, sem wait: API disabled in the context of init_worker_by_lua*, ", "", ] === TEST 8: semaphore in init_worker_by_lua (new and post) --- http_config lua_package_path $TEST_NGINX_LUA_PACKAGE_PATH; init_worker_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem new: ", err) end sem:post(2) local count = sem:count() ngx.log(ngx.WARN, "sem count: ", count) package.loaded.my_sema = sem } --- config location /t { content_by_lua_block { local sem = package.loaded.my_sema local ok, err = sem:wait(0.1) if not ok then ngx.say("failed to wait: ", err) return end ngx.say("sem wait successfully.") } } --- request GET /t --- response_body sem wait successfully. --- grep_error_log eval: qr/sem \w+: .*?,/ --- grep_error_log_out eval [ "sem count: 2, ", "" ] --- no_error_log [error] === TEST 9: semaphore in set_by_lua (wait is not allowed) --- http_config eval: $::HttpConfig --- config location /t { set_by_lua_block $res { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } echo "ok"; } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: 1, sem: API disabled in the context of set_by_lua*, ", "sem: 1, sem: API disabled in the context of set_by_lua*, ", ] === TEST 10: semaphore in rewrite_by_lua (all allowed) --- http_config eval: $::HttpConfig --- config location /t { rewrite_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end local ok, err = sem:wait(0.01) if not ok then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } echo "ok"; } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: timeout, sem: 1, ", "sem: timeout, sem: 1, ", ] === TEST 11: semaphore in access_by_lua (all allowed) --- http_config eval: $::HttpConfig --- config location /t { access_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end local ok, err = sem:wait(0.01) if not ok then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } echo "ok"; } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: timeout, sem: 1, ", "sem: timeout, sem: 1, ", ] === TEST 12: semaphore in content_by_lua (all allowed) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end local ok, err = sem:wait(0.01) if not ok then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) else ngx.say("ok") end } } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: timeout, sem: 1, ", "sem: timeout, sem: 1, ", ] === TEST 13: semaphore in log_by_lua (wait not allowed) --- http_config eval: $::HttpConfig --- config location /t { echo "ok"; log_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: 1 while logging request, sem: API disabled in the context of log_by_lua* while logging request, ", "sem: 1 while logging request, sem: API disabled in the context of log_by_lua* while logging request, ", ] --- wait: 0.2 === TEST 14: semaphore in header_filter_by_lua (wait not allowed) --- http_config eval: $::HttpConfig --- config location /t { echo "ok"; header_filter_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: 1, sem: API disabled in the context of header_filter_by_lua*, ", "sem: 1, sem: API disabled in the context of header_filter_by_lua*, ", ] === TEST 15: semaphore in body_filter_by_lua (wait not allowed) --- http_config eval: $::HttpConfig --- config location /t { echo "ok"; body_filter_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: 1, sem: API disabled in the context of body_filter_by_lua*, sem: 1, sem: API disabled in the context of body_filter_by_lua*, ", "sem: 1, sem: API disabled in the context of body_filter_by_lua*, sem: 1, sem: API disabled in the context of body_filter_by_lua*, ", ] === TEST 16: semaphore in ngx.timer (all allowed) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua_block { local function func_sem() local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, "sem: ", err) end local ok, err = sem:wait(0.01) if not ok then ngx.log(ngx.ERR, "sem: ", err) end sem:post(1) local count = sem:count() ngx.log(ngx.ERR, "sem: ", count) local ok, err = sem:wait(0.1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end end local ok, err = ngx.timer.at(0, func_sem) if ok then ngx.sleep(0.01) ngx.say("ok") end } } --- request GET /t --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: timeout, sem: 1, ", "sem: timeout, sem: 1, ", ] --- wait: 0.2 === TEST 17: semaphore post in all phase (in a request) --- http_config lua_package_path $TEST_NGINX_LUA_PACKAGE_PATH; init_worker_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) end package.loaded.sem = sem local function wait() local i = 0 while true do local ok, err = sem:wait(1) if not ok then ngx.log(ngx.ERR, "sem: ", err) end i = i + 1 if i % 6 == 0 then ngx.log(ngx.ERR, "sem: 6 times") end end end local ok, err = ngx.timer.at(0, wait) if not ok then ngx.log(ngx.ERR, "sem: ", err) end } --- config location /test { set_by_lua_block $res { local sem = package.loaded.sem sem:post() } rewrite_by_lua_block { local sem = package.loaded.sem sem:post() } access_by_lua_block { local sem = package.loaded.sem sem:post() } content_by_lua_block { local sem = package.loaded.sem sem:post() ngx.say("ok") } header_filter_by_lua_block { local sem = package.loaded.sem sem:post() } body_filter_by_lua_block { local sem = package.loaded.sem sem:post() } } --- request GET /test --- response_body ok --- grep_error_log eval: qr/sem: .*?,/ --- grep_error_log_out eval [ "sem: 6 times, ", "sem: 6 times, ", ] --- wait: 0.2 === TEST 18: semaphore wait post in access_by_lua --- http_config eval: $::HttpConfig --- config location /test { access_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) local func_wait = function () ngx.say("enter wait") local ok, err = sem:wait(1) if ok then ngx.say("wait success") end end local func_post = function () ngx.say("enter post") sem:post() ngx.say("post success") end local co1 = ngx.thread.spawn(func_wait) local co2 = ngx.thread.spawn(func_post) ngx.thread.wait(co1) ngx.thread.wait(co2) } } --- request GET /test --- response_body enter wait enter post post success wait success --- no_error_log [error] === TEST 19: semaphore wait post in rewrite_by_lua --- http_config eval: $::HttpConfig --- config location /t { rewrite_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) local func_wait = function () ngx.say("enter wait") local ok, err = sem:wait(1) if ok then ngx.say("wait success") end end local func_post = function () ngx.say("enter post") sem:post() ngx.say("post success") end local co1 = ngx.thread.spawn(func_wait) local co2 = ngx.thread.spawn(func_post) ngx.thread.wait(co1) ngx.thread.wait(co2) } } --- request GET /test --- response_body enter wait enter post post success wait success --- no_error_log [error] === TEST 20: semaphore wait in timer.at --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new() local function func_wait(premature) local ok, err = sem:wait(1) if not ok then ngx.log(ngx.ERR, err) else ngx.log(ngx.ERR, "wait success") end end ngx.timer.at(0, func_wait) sem:post() ngx.sleep(0.01) ngx.say("ok") } } --- request GET /test --- response_body ok --- error_log wait success === TEST 21: semaphore post in header_filter_by_lua (subrequest) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local res1, res2 = ngx.location.capture_multi{ {"/sem_wait"}, {"/sem_post"}, } ngx.say(res1.status) ngx.say(res1.body) ngx.say(res2.status) ngx.say(res2.body) } } location /sem_wait { content_by_lua_block { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.say(err) ngx.exit(500) end package.loaded.sem = sem end local sem = package.loaded.sem local ok, err = sem:wait(1) if ok then ngx.print("wait") ngx.exit(200) else ngx.exit(500) end } } location /sem_post { header_filter_by_lua_block { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) end package.loaded.sem = sem end local sem = package.loaded.sem sem:post() } content_by_lua_block { ngx.print("post") ngx.exit(200) } } --- request GET /test --- response_body 200 wait 200 post --- no_error_log [error] === TEST 22: semaphore post in body_filter_by_lua (subrequest) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local res1, res2 = ngx.location.capture_multi{ {"/sem_wait"}, {"/sem_post"}, } ngx.say(res1.status) ngx.say(res1.body) ngx.say(res2.status) ngx.say(res2.body) } } location /sem_wait { content_by_lua_block { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.say(err) ngx.exit(500) end package.loaded.sem = sem end local sem = package.loaded.sem local ok, err = sem:wait(10) if ok then ngx.print("wait") ngx.exit(200) else ngx.exit(500) end } } location /sem_post { body_filter_by_lua_block { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) end package.loaded.sem = sem end local sem = package.loaded.sem sem:post() } content_by_lua_block { ngx.print("post") ngx.exit(200) } } --- request GET /test --- response_body 200 wait 200 post --- log_level: debug --- no_error_log [error] === TEST 23: semaphore post in set_by_lua --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local res1, res2 = ngx.location.capture_multi{ {"/sem_wait"}, {"/sem_post"}, } ngx.say(res1.status) ngx.say(res1.body) ngx.say(res2.status) ngx.say(res2.body) } } location /sem_wait { content_by_lua_block { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.say(err) ngx.exit(500) end package.loaded.sem = sem end local sem = package.loaded.sem local ok, err = sem:wait(10) if ok then ngx.print("wait") ngx.exit(200) else ngx.exit(500) end } } location /sem_post { set_by_lua_block $res { local semaphore = require "ngx.semaphore" if not package.loaded.sem then local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) end package.loaded.sem = sem end local sem = package.loaded.sem sem:post() } content_by_lua_block { ngx.print("post") ngx.exit(200) } } --- request GET /test --- response_body 200 wait 200 post --- log_level: debug --- no_error_log [error] === TEST 24: semaphore post in timer.at --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" package.loaded.sem = semaphore.new(0) local res1, res2 = ngx.location.capture_multi{ {"/sem_wait"}, {"/sem_post"}, } ngx.say(res1.status) ngx.say(res1.body) ngx.say(res2.status) ngx.say(res2.body) } } location /sem_wait { content_by_lua_block { local sem = package.loaded.sem local ok, err = sem:wait(2) if ok then ngx.print("wait") ngx.exit(200) else ngx.status = 500 ngx.say(err) end } } location /sem_post { content_by_lua_block { local function func(premature) local sem = package.loaded.sem sem:post() end ngx.timer.at(0, func, g) ngx.sleep(0) ngx.print("post") ngx.exit(200) } } --- request GET /test --- response_body 200 wait 200 post --- log_level: debug --- no_error_log [error] === TEST 25: two thread wait for each other --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem_A = semaphore.new(0) local sem_B = semaphore.new(0) if not sem_A or not sem_B then error("create failed") end local function th_A() for i = 1, 11 do local ok, err = sem_A:wait(1) if not ok then ngx.log(ngx.ERR, err) end sem_B:post(1) end ngx.say("count in A: ", sem_A:count()) end local function th_B() for i = 1, 10 do local ok, err = sem_B:wait(1) if not ok then ngx.log(ngx.ERR, err) end sem_A:post(1) end ngx.say("count in B: ", sem_B:count()) end local co_A = ngx.thread.spawn(th_A) local co_B = ngx.thread.spawn(th_B) sem_A:post(1) } } --- log_level: debug --- request GET /test --- response_body count in B: 0 count in A: 0 --- no_error_log [error] === TEST 26: kill a light thread that is waiting on a semaphore(no resource) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if not sem then error("create failed") end local function func_wait() sem:wait(1) end local co = ngx.thread.spawn(func_wait) local ok, err = ngx.thread.kill(co) if ok then ngx.say("ok") else ngx.say(err) end } } --- log_level: debug --- request GET /test --- response_body ok --- no_error_log [error] === TEST 27: kill a light thread that is waiting on a semaphore(after post) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if not sem then error("create failed") end local function func_wait() sem:wait(1) end local co = ngx.thread.spawn(func_wait) sem:post() local ok, err = ngx.thread.kill(co) if ok then ngx.say("ok") else ngx.say(err) end ngx.sleep(0.01) local count = sem:count() ngx.say("count: ", count) } } --- log_level: debug --- request GET /test --- response_body ok count: 1 --- no_error_log [error] === TEST 28: kill a thread that is waiting on another thread that is waiting on semaphore --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if not sem then error("create failed") end local function sem_wait() ngx.say("sem waiting start") local ok, err = sem:wait(0.1) if not ok then ngx.say("sem wait err: ", err) end ngx.say("sem waiting done") end local function thread_wait() local co = ngx.thread.spawn(sem_wait) ngx.say("thread waiting start") local ok, err = ngx.thread.wait(co) if not ok then ngx.say("thread wait err: ", err) end ngx.say("thread waiting done") end local co2 = ngx.thread.spawn(thread_wait) ngx.sleep(0.01) local ok, err = ngx.thread.kill(co2) if ok then ngx.say("thread kill success") else ngx.say("kill err: ", err) end } } --- log_level: debug --- request GET /test --- response_body sem waiting start thread waiting start thread kill success sem wait err: timeout sem waiting done --- no_error_log [error] === TEST 29: a light thread that is going to exit is waiting on a semaphore --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if not sem then error("create failed") end local function func(sem) ngx.say("sem waiting") local ok, err = sem:wait(0.1) if ok then ngx.say("wait success") else ngx.say("err: ", err) end end local co = ngx.thread.spawn(func, sem) ngx.say("ok") ngx.exit(200) } } --- log_level: debug --- request GET /test --- response_body sem waiting ok --- error_log http lua semaphore cleanup === TEST 30: main thread wait a light thread that is waiting on a semaphore --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if not sem then error("create failed") end local function func(sem) local ok, err = sem:wait(0.001) if ok then ngx.say("wait success") else ngx.say("err: ", err) end end local co = ngx.thread.spawn(func, sem) ngx.thread.wait(co) } } --- log_level: debug --- request GET /test --- response_body err: timeout --- no_error_log [error] === TEST 31: multi wait and mult post with one semaphore --- http_config eval: $::HttpConfig --- config location = /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if not sem then ngx.log(ngx.ERR, err) ngx.exit(500) end local function func(op, id) ngx.say(op, ": ", id) if op == "wait" then local ok, err = sem:wait(1) if ok then ngx.say("wait success: ", id) end else sem:post() end end local tco = {} for i = 1, 3 do tco[#tco + 1] = ngx.thread.spawn(func, "wait", i) end for i = 1, 3 do tco[#tco + 1] = ngx.thread.spawn(func, "post", i) end for i = 1, #tco do ngx.thread.wait(tco[i]) end } } --- request GET /test --- response_body wait: 1 wait: 2 wait: 3 post: 1 post: 2 post: 3 wait success: 1 wait success: 2 wait success: 3 --- no_error_log [error] === TEST 32: semaphore wait time is zero --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) local ok, err = sem:wait(0) if not ok then ngx.say(err) end } } --- request GET /test --- response_body timeout --- no_error_log [error] === TEST 33: test semaphore gc --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem, err = semaphore.new(0) if sem then ngx.say("success") end sem = nil collectgarbage("collect") } } --- request GET /test --- response_body success --- log_level: debug --- error_log in lua gc, semaphore === TEST 34: basic semaphore_mm alloc --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(0) if sem then ngx.say("ok") end } } --- log_level: debug --- request GET /test --- response_body ok --- grep_error_log eval: qr/(new block, alloc semaphore|from head of free queue, alloc semaphore)/ --- grep_error_log_out eval [ "new block, alloc semaphore ", "from head of free queue, alloc semaphore ", ] === TEST 35: basic semaphore_mm free insert tail --- http_config eval: $::HttpConfig --- config location /t { content_by_lua_block { local semaphore = require "ngx.semaphore" local sems = package.loaded.sems or {} package.loaded.sems = sems local num_per_block = 4095 if not sems[num_per_block] then for i = 1, num_per_block * 3 do sems[i] = semaphore.new(0) end end for i = 1, 2 do if sems[i] then sems[i] = nil ngx.say("ok") break end end collectgarbage("collect") } } --- log_level: debug --- request GET /t --- response_body ok --- error_log add to free queue tail === TEST 36: basic semaphore_mm free insert head --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sems = package.loaded.sems or {} package.loaded.sems = sems local num_per_block = 4095 if not sems[num_per_block] then for i = 1, num_per_block * 3 do sems[i] = semaphore.new(0) end end if sems[#sems] then sems[#sems] = nil ngx.say("ok") end collectgarbage("collect") } } --- log_level: debug --- request GET /test --- response_body ok --- error_log add to free queue head === TEST 37: semaphore_mm free block (load <= 50% & the on the older side) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sems = package.loaded.sems or {} package.loaded.sems = sems local num_per_block = 4095 if not sems[num_per_block * 3] then for i = 1, num_per_block * 3 do sems[i] = semaphore.new(0) end for i = num_per_block + 1, num_per_block * 2 do sems[i] = nil end else for i = 1, num_per_block do sems[i] = nil end end collectgarbage("collect") ngx.say("ok") } } --- log_level: debug --- request GET /test --- response_body ok --- grep_error_log eval: qr/free semaphore block/ --- grep_error_log_out eval [ "", "free semaphore block ", ] --- timeout: 10 === TEST 38: basic semaphore count --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new(10) local count = sem:count() ngx.say(count) sem:wait(0) local count = sem:count() ngx.say(count) sem:post(3) local count = sem:count() ngx.say(count) } } --- request GET /test --- response_body 10 9 12 --- no_error_log [error] === TEST 39: basic semaphore count(negative number) --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local sem = semaphore.new() local count = sem:count() ngx.say(count) local function wait() sem:wait(0.01) end local co = ngx.thread.spawn(wait) local count = sem:count() ngx.say(count) } } --- request GET /test --- response_body 0 -1 --- no_error_log [error] === TEST 40: bugfix: semaphore instance can't be garbage collected when someone is waiting on it --- http_config eval: $::HttpConfig --- config location /test { content_by_lua_block { local semaphore = require "ngx.semaphore" local my_sema = {} local key = "my key" local function my_clean() print("cleaning up") my_sema[key]:post() my_sema[key] = nil collectgarbage() end local ok, err = ngx.timer.at(0.001, my_clean) if not ok then ngx.log(ngx.ERR, "failed to create timer: ", err) ngx.exit(500) end my_sema[key] = semaphore:new(0) local ok, err = my_sema[key]:wait(2) ngx.say(ok, ", ", err) } } --- request GET /test --- response_body true, nil --- no_error_log [error] [crit]