ngx.timer.at args passed as refs ?

550 views
Skip to first unread message

Dragos Dascalita Haut

unread,
Jun 2, 2014, 11:10:44 PM6/2/14
to openre...@googlegroups.com
Hi, 
I was reading the documentation to understand if the arguments that can be passed to ngx.timer.at are going to be allocated extra memory, or are simply passed as references ?

In my context I'm trying to use `ngx.socket.tcp` in log_by_lua, which is not enabled of course; so I'm looking to trigger a timer and I'm wondering if I can pass the `self` argument to the timer, where `self` is an instance of a pseudo lua class. 

Thanks,
Dragos Dascalita Haut 

Jerome Lafon

unread,
Jun 3, 2014, 6:13:52 AM6/3/14
to openre...@googlegroups.com
Hello,
I believe you can only pass only nil, booleans, numbers and strings parameters to the function used by ngx.timer.at because they are passed by value, not by reference.
Jérôme

Brian Akins

unread,
Jun 3, 2014, 8:58:54 AM6/3/14
to openre...@googlegroups.com
You can use a closure as your timer function - it can have whatever variables you need in it.

Jerome Lafon

unread,
Jun 3, 2014, 10:02:03 AM6/3/14
to openre...@googlegroups.com
Could you write an example with a table as a parameter please?

Jérôme

Dragos Dascalita Haut

unread,
Jun 3, 2014, 3:10:40 PM6/3/14
to openre...@googlegroups.com
Sure, here's a test where a metatable is passed as an arg to timer:

=== TEST 2: test ngx timer with metatable args
--- http_config eval: $::HttpConfig
--- config
        location /t {

            content_by_lua '
                local _M = {}
                function _M:new(o)
                    o = o or {}
                    setmetatable(o, self)
                    self.__index = self
                    self.message = "I am being executed via timer"
                    return o
                end
                local function timer_callback(premature, self)
                    ngx.log(ngx.WARN, self.message)
                end

                function _M:doSomethingAsync()
                   -- THIS IS WHERE self IS PASSED TO TIMER
                   ngx.timer.at(0.001, timer_callback, self)
                end

                local mInst = _M:new()
                mInst:doSomethingAsync()
                ngx.say("timer is pending")
                -- wait for the async to happen
                ngx.sleep(0.100)
            ';


        }
--- request
GET /t
--- response_body
timer is pending
--- error_code: 200
--- no_error_log
[error]
--- grep_error_log eval: qr/I am being executed via timer *?/
--- grep_error_log_out
I am being executed via timer

Result: PASS


If 'self' is passed as ref it's ok, but if it's going through a deep copy then it's not so performant, right ?

Thanks,
Dragos

Yichun Zhang (agentzh)

unread,
Jun 3, 2014, 4:32:24 PM6/3/14
to openresty-en
Hello!

On Mon, Jun 2, 2014 at 8:10 PM, Dragos Dascalita Haut wrote:
> I was reading the documentation to understand if the arguments that can be
> passed to ngx.timer.at are going to be allocated extra memory, or are simply
> passed as references ?
>

Just added some more documentation to clarify this:

https://github.com/openresty/lua-nginx-module#ngxtimerat

To quote,

"You can pass most of the standard Lua values (nils, booleans,
numbers, strings, tables, closures, file handles, and etc) into the
timer callback, either explicitly as user arguments or implicitly as
upvalues for the callback closure. There are several exceptions,
however: you cannot pass any thread objects returned
bycoroutine.create and ngx.thread.spawn or any cosocket objects
returned by ngx.socket.tcp, ngx.socket.udp, andngx.req.socket because
these objects' lifetime is bound to the request context creating them
while the timer callback is detached from the creating request's
context (by design) and runs in its own (fake) request context. If you
try to share the thread or cosocket objects across the boundary of the
creating request, then you will get the "no co ctx found" error (for
threads) or "bad request" (for cosockets). It is fine, however, to
creating all these objects inside your timer callback."

Best regards,
-agentzh

Jerome Lafon

unread,
Jun 4, 2014, 8:30:15 AM6/4/14
to openre...@googlegroups.com
Thanks a lot.
Jérôme


Le mardi 3 juin 2014 05:10:44 UTC+2, Dragos Dascalita Haut a écrit :

Dragos Dascalita Haut

unread,
Jun 4, 2014, 7:41:05 PM6/4/14
to openre...@googlegroups.com
Thanks for making the documentation clearer @agentzh !
Now that we know what variables can be passed to the callback, the other remaining item was if they're passed as reference or copied.
When the timer runs I'm seeing in ngx_http_lua_timer.c a 
   ngx_memcpy(&tctx, ev->data, sizeof(ngx_http_lua_timer_ctx_t));
which makes me assume that data is copied when the timer runs. Feel free to correct me if I'm wrong.

Thanks,
Dragos

Yichun Zhang (agentzh)

unread,
Jun 4, 2014, 8:08:36 PM6/4/14
to openresty-en
Hello!

On Wed, Jun 4, 2014 at 4:41 PM, Dragos Dascalita Haut wrote:
> Thanks for making the documentation clearer @agentzh !
> Now that we know what variables can be passed to the callback, the other
> remaining item was if they're passed as reference or copied.

This is exactly the same when you pass values into a normal Lua
function. GC objects like tables, strings, and closures are never
copied and they are subject to Lua GC. No surprise is going on here.

> When the timer runs I'm seeing in ngx_http_lua_timer.c a
> ngx_memcpy(&tctx, ev->data, sizeof(ngx_http_lua_timer_ctx_t));
> which makes me assume that data is copied when the timer runs.

This line of C source is irrelevant, which just copies the internal
timer meta data used *inside* ngx_lua.

Regards,
-agentzh

Dascalita Dragos

unread,
Jun 4, 2014, 9:39:10 PM6/4/14
to openre...@googlegroups.com
Thanks a lot for the explanation !​
Reply all
Reply to author
Forward
0 new messages