@@ -14,7 +14,7 @@ local anchor_registry = {}
1414local gc_registry = setmetatable ({},{ __mode = " v" })
1515local timer_id = 0
1616local now = ngx .now
17- local sleep = ngx .sleep
17+ local ngx_sleep = ngx .sleep
1818local exiting = ngx .worker .exiting
1919
2020local KEY_PREFIX = " [lua-resty-timer]"
@@ -23,6 +23,54 @@ local CANCEL_GC = "GC"
2323local CANCEL_SYSTEM = " SYSTEM"
2424local CANCEL_USER = " USER"
2525
26+ local sleep do
27+ -- create a 10yr timer only called with `premature` set. The callback will
28+ -- release a global semaphore to wake up sleeping threads
29+ local sema = assert (require (" ngx.semaphore" ).new ())
30+ assert (timer_at (10 * 365 * 24 * 60 * 60 , function ()
31+ sema :post (math.huge )
32+
33+ -- TODO: remove the below log line, it's an ugly hack around semaphores
34+ -- not being released properly, an Openresty bug.
35+ -- See https://github.com/openresty/lua-resty-core/issues/337
36+ ngx .timer .at (0 , function ()
37+ ngx .log (ngx .WARN ," please ignore, just generating IO, see https://github.com/openresty/lua-resty-core/issues/337" )
38+ end )
39+ end ))
40+
41+ --- A `sleep` function that exits early on system exit. The same as `ngx.sleep`
42+ -- except that it will be interrupted when the current worker starts exiting.
43+ -- @param delay same as `ngx.sleep()`; delay in seconds.
44+ -- @return results of `ngx.worker.exiting()`
45+ -- @usage if sleep(5) then
46+ -- -- sleep was interrupted, exit now
47+ -- return nil, "exiting"
48+ -- end
49+ --
50+ -- -- do stuff
51+ function sleep (delay )
52+ if type (delay ) ~= " number" then
53+ error (" Bad argument #1, expected number, got " .. type (delay ), 2 )
54+ end
55+
56+ if delay <= 0 then
57+ ngx_sleep (delay )
58+ return exiting ()
59+ end
60+
61+ local _ , err = sema :wait (delay )
62+ if err == " timeout" then
63+ return false
64+ end
65+
66+ if err then
67+ ngx .log (ngx .ERR , " waiting for semaphore failed: " , err )
68+ end
69+
70+ return exiting ()
71+ end
72+ end
73+
2674
2775
2876--- Cancel the timer.
@@ -141,10 +189,7 @@ local function handler(premature, id)
141189 end
142190
143191 -- existing timer recurring, so keep this thread alive and just sleep
144- if not exiting () then
145- sleep (next_interval )
146- end
147- premature = exiting ()
192+ premature = sleep (next_interval )
148193 end -- while
149194end
150195
340385return setmetatable (
341386 {
342387 new = new ,
388+ sleep = sleep ,
343389 CANCEL_GC = CANCEL_GC ,
344390 CANCEL_SYSTEM = CANCEL_SYSTEM ,
345391 CANCEL_USER = CANCEL_USER ,
0 commit comments