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

Invalid coroutine.resume return values

Open semyon422 opened this issue 9 months ago • 2 comments

When I create a coroutine using coroutine.wrap and then resume it, coroutine.resume skips the first "status" value and returns all other values. As docs says it must return the first "status" boolean value. I use openresty from AUR: aur/openresty 1.27.1.1-1

$ curl http://localhost:8080/1
2, nil, 3, nil, false, cannot resume dead coroutine
$ curl http://localhost:8080/2
true, 2, true, 3, false, cannot resume dead coroutine
worker_processes 1;
error_log error.log notice;
pid nginx.pid;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        lua_code_cache off;

        location /1 {
            content_by_lua_block {
                local co

                local function f()
                    co = coroutine.running()
                    coroutine.yield(1)
                    coroutine.yield(2)
                    return 3
                end

                assert(coroutine.wrap(f)() == 1)

                local ok1, ret1 = coroutine.resume(co)
                local ok2, ret2 = coroutine.resume(co)
                local ok3, ret3 = coroutine.resume(co)
                ngx.say(("%s, %s, %s, %s, %s, %s"):format(
                    ok1, ret1, ok2, ret2, ok3, ret3
                ))
            }
        }

        location /2 {
            content_by_lua_block {
                local co

                local function f()
                    co = coroutine.running()
                    coroutine.yield(1)
                    coroutine.yield(2)
                    return 3
                end

                do
                    local c = coroutine.create(f)
                    local ok, v = assert(coroutine.resume(c))
                    assert(v == 1)
                end

                local ok1, ret1 = coroutine.resume(co)
                local ok2, ret2 = coroutine.resume(co)
                local ok3, ret3 = coroutine.resume(co)
                ngx.say(("%s, %s, %s, %s, %s, %s"):format(
                    ok1, ret1, ok2, ret2, ok3, ret3
                ))
            }
        }
    }
}

semyon422 avatar Apr 02 '25 12:04 semyon422

Lua-nginx module uses a flag is_wrap in ngx_http_lua_coroutine_wrap to indicate that the coroutine is created by coroutine.wrap https://github.com/openresty/lua-nginx-module/blob/9600893c310f6865efa7f799b1e6426bfd73b220/src/ngx_http_lua_coroutine.c#L97

When this flag is set, the first boolean value will not be returned, even when this coroutine is later resumed by coroutine.resume https://github.com/openresty/lua-nginx-module/blob/9600893c310f6865efa7f799b1e6426bfd73b220/src/ngx_http_lua_util.c#L1343

One solution for this issue is always to set is_wrap to false in ngx_http_lua_coroutine_resume, and only set it to true at the end of ngx_http_lua_coroutine_wrap_runner function, but I'm not sure whether this solution would be acceptable to the OpenResty team.

gavin0616 avatar Aug 09 '25 02:08 gavin0616

I checked the Lua manual, and it doesn't explicitly state that coroutines created with wrap can be used in resume. Moreover, this approach appears to undermine the encapsulation provided by wrap. It seems the usage above might be more like undefined behavior?

gavin0616 avatar Sep 05 '25 06:09 gavin0616