lua-nginx-module icon indicating copy to clipboard operation
lua-nginx-module copied to clipboard

return unexpected error when ngx.thread.wait() with multipule ngx.thread

Open youjujun opened this issue 6 years ago • 5 comments

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 avatar Dec 10 '18 05:12 youjujun

@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.

agentzh avatar Feb 04 '19 06:02 agentzh

BTW, sorry for the late reply.

agentzh avatar Feb 04 '19 06:02 agentzh

@youjujun Your suggested "fix" does not make any sense to me since that's not the expected behavior by design.

agentzh avatar Feb 04 '19 06:02 agentzh

@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.

@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.

@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.

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

eric-strive avatar Jul 18 '22 14:07 eric-strive

@agentzh Do you have any good suggestions?

eric-strive avatar Jul 18 '22 15:07 eric-strive