lua-nginx-module
lua-nginx-module copied to clipboard
return unexpected error when ngx.thread.wait() with multipule ngx.thread
parent thread exec ngx.thread.wait(t1, t2), it only wait for the first thread, without considering other threads it waiting for. This will cause the parent thread beeing waked up by other thread unexpected. --------------------Bug describing---------------------- for example: nginx.conf location as follows: location = /mytest { allow 127.0.0.1; deny all; content_by_lua_file lua/mytest.lua; }
Then touch lua/mytest.lua, with the follow content: #cat lua/mytest.lua function f() ngx.sleep(0.1) return "I shouldn't be seen" end
local t1 = ngx.thread.spawn(f) if not t1 then return end
local t2 = ngx.thread.spawn(f) if not t2 then return end
ok = ngx.thread.wait(t1, t2) if not ok then return end
_, ret = ngx.sleep(3) -- ### expect: sleep 3s, then return: ret is nil ngx.say(ret)
Then, start nginx-lua, and run # time curl 127.0.0.1:80/mytest #time curl 127.0.0.1:80/mytest I shouldn't be seen ## ### ERROR 1: ngx.say(ret) output a unexpected string.
real 0m0.147s ## ### ERROR2: sleep(3) returns quickly, without sleeping 3s user 0m0.006s sys 0m0.037s
Error hanppens as ERROR1 and ERROR2. ERROR 1: ngx.say(ret) output a unexpected string: I shouldn't be seen ERROR2: sleep(3) returns quickly, without sleeping 3s
what's more, if we replace ngx.sleep with
local sock = ngx.socket.tcp ..... ngx.thread.wait(t1, t2) data = sock.receive
The same errors happen too.
--------------------Bug analysis---------------------- First parent thread stops at ngx.thread.wait(t1, t2); Then thread t1 wake up the parent thread, Then parent thread stops at ngx.sleep(3), Then thread t2 wake up the parent thread, Then the code ngx.sleep(3) returns quickly, with return value of thread t2!!
--------------------Code analysis---------------------- ` ngx_int_t ngx_http_lua_run_thread() { user_co_done: nrets = lua_gettop(ctx->cur_co_ctx->co);
next_coctx = ctx->cur_co_ctx->parent_co_ctx;
if (next_coctx == NULL) {
/* being a light thread */
goto no_parent;
}
next_co = next_coctx->co;
/*
* ended successful, coroutine.resume returns true plus
* any return values
*/
lua_pushboolean(next_co, success);
if (nrets) {
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
}
if (ctx->cur_co_ctx->is_uthread) {
ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
ctx->uthreads--;
}
nrets++;
ctx->cur_co_ctx = next_coctx;
ngx_http_lua_probe_info("set parent running");
// Need TO consider: clear or modify other threads' coctx->waited_by_parent!!!!
next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
} `
Before the last code "next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING;", we need to :
consider to clear or modify the flags of other threads' coctx->waited_by_parent!
@youjujun It is by design. The wait function returns immediately upon the first terminated (or aborted) light thread.
I tried your Lua example above on my side. It works as expected:
$ curl -i -sS http://127.0.0.1:1984/test
HTTP/1.1 200 OK
Server: nginx/1.15.8 (no pool)
Date: Mon, 04 Feb 2019 06:10:33 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
I shouldn't be seen
The output "I shouldn't be seen" is expected because light threads are running concurrently by design. Even after the main thread's wait() returns, the other light thread running f is still running. You need to kill the other thread after wait() returns if that's what you want.
BTW, sorry for the late reply.
@youjujun Your suggested "fix" does not make any sense to me since that's not the expected behavior by design.
@youjujun It is by design. The
waitfunction returns immediately upon the first terminated (or aborted) light thread.I tried your Lua example above on my side. It works as expected:
$ curl -i -sS http://127.0.0.1:1984/test HTTP/1.1 200 OK Server: nginx/1.15.8 (no pool) Date: Mon, 04 Feb 2019 06:10:33 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive I shouldn't be seenThe output "I shouldn't be seen" is expected because light threads are running concurrently by design. Even after the main thread's wait() returns, the other light thread running
fis still running. You need to kill the other thread afterwait()returns if that's what you want.
@youjujun It is by design. The
waitfunction returns immediately upon the first terminated (or aborted) light thread.I tried your Lua example above on my side. It works as expected:
$ curl -i -sS http://127.0.0.1:1984/test HTTP/1.1 200 OK Server: nginx/1.15.8 (no pool) Date: Mon, 04 Feb 2019 06:10:33 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive I shouldn't be seenThe output "I shouldn't be seen" is expected because light threads are running concurrently by design. Even after the main thread's wait() returns, the other light thread running
fis still running. You need to kill the other thread afterwait()returns if that's what you want.
@youjujun It is by design. The
waitfunction returns immediately upon the first terminated (or aborted) light thread.I tried your Lua example above on my side. It works as expected:
$ curl -i -sS http://127.0.0.1:1984/test HTTP/1.1 200 OK Server: nginx/1.15.8 (no pool) Date: Mon, 04 Feb 2019 06:10:33 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive I shouldn't be seenThe output "I shouldn't be seen" is expected because light threads are running concurrently by design. Even after the main thread's wait() returns, the other light thread running
fis still running. You need to kill the other thread afterwait()returns if that's what you want.
Hi, I'm having a similar problem, Here's the thing local function f(body) return ngx.location.capture("/sync/db_common_ad_api", { method = ngx.HTTP_POST, body = body }) end local body1 = "The request of 0.5 seconds" local t1 = ngx.thread.spawn(f,body1) if not t1 then return end
local body2 = "The request of 0.5 seconds" local t2 = ngx.thread.spawn(f,body2) if not t2 then return end
local ok = ngx.thread.wait(t1, t2) if not ok then return end local body3 = "The request of 3 seconds" local ok2 = ngx.thread.spawn(f,body3) if not ok2 then return end local ok_wait, ret = ngx.thread.wait(ok2) ngx.say(ret) ngx.location.capture cannot be kill, ret may not be what I want to get, Any good suggestions here
@agentzh Do you have any good suggestions?