From 88d71b0e775acf7b20da40e60b001dd39c790d81 Mon Sep 17 00:00:00 2001 From: spacewander Date: Tue, 28 Jul 2020 17:31:36 +0800 Subject: [PATCH] feature: allow ngx.sleep to be used blockingly in non-yieldable phases Allow ngx.sleep everywhere simplify the application's logic. Now we don't need to write a fallback if the same function need to be run in non-yieldable phases. Close #1730. --- README.markdown | 7 ++-- doc/HttpLuaModule.wiki | 7 ++-- src/ngx_http_lua_sleep.c | 23 ++++++++---- t/077-sleep.t | 76 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 96 insertions(+), 17 deletions(-) diff --git a/README.markdown b/README.markdown index bd29909b53..a446f62d6f 100644 --- a/README.markdown +++ b/README.markdown @@ -5621,14 +5621,17 @@ ngx.sleep **syntax:** *ngx.sleep(seconds)* -**context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua** +**context:** *init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua** -Sleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond). +Sleeps for the specified seconds without blocking in yieldable phases or blockingly in other phases. +One can specify time resolution up to 0.001 seconds (i.e., one millisecond). Behind the scene, this method makes use of the Nginx timers. Since the `0.7.20` release, The `0` time argument can also be specified. +Since the `FIXME` release, this method can be used in non-yieldable phases blockingly. + This method was introduced in the `0.5.0rc30` release. [Back to TOC](#nginx-api-for-lua) diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 04a2694972..2fbffc2c19 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -4720,14 +4720,17 @@ Since v0.8.3 this function returns 1 on success, or re '''syntax:''' ''ngx.sleep(seconds)'' -'''context:''' ''rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*'' +'''context:''' ''init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*, exit_worker_by_lua*'' -Sleeps for the specified seconds without blocking. One can specify time resolution up to 0.001 seconds (i.e., one millisecond). +Sleeps for the specified seconds without blocking in yieldable phases or blockingly in other phases. +One can specify time resolution up to 0.001 seconds (i.e., one millisecond). Behind the scene, this method makes use of the Nginx timers. Since the 0.7.20 release, The 0 time argument can also be specified. +Since the FIXME release, this method can be used in non-yieldable phases blockingly. + This method was introduced in the 0.5.0rc30 release. == ngx.escape_uri == diff --git a/src/ngx_http_lua_sleep.c b/src/ngx_http_lua_sleep.c index b8601fee95..6fbbecc6d8 100644 --- a/src/ngx_http_lua_sleep.c +++ b/src/ngx_http_lua_sleep.c @@ -36,23 +36,34 @@ ngx_http_lua_ngx_sleep(lua_State *L) return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n); } - r = ngx_http_lua_get_req(L); - if (r == NULL) { - return luaL_error(L, "no request found"); - } - delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000); if (delay < 0) { return luaL_error(L, "invalid sleep duration \"%d\"", delay); } + r = ngx_http_lua_get_req(L); + if (r == NULL) { + /* init_by_lua phase */ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "lua ready to sleep for %d ms", delay); + + ngx_msleep(delay); + return 0; + } + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } - ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_YIELDABLE); + if (!(ctx->context & NGX_HTTP_LUA_CONTEXT_YIELDABLE)) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua ready to sleep for %d ms", delay); + + ngx_msleep(delay); + return 0; + } coctx = ctx->cur_co_ctx; if (coctx == NULL) { diff --git a/t/077-sleep.t b/t/077-sleep.t index 96f04bd98e..5518b09406 100644 --- a/t/077-sleep.t +++ b/t/077-sleep.t @@ -9,7 +9,7 @@ log_level('debug'); repeat_each(2); -plan tests => repeat_each() * 71; +plan tests => repeat_each() * (blocks() * 4); #no_diff(); no_long_string(); @@ -237,21 +237,20 @@ lua sleep timer expired: "/test?" -=== TEST 10: ngx.sleep unavailable in log_by_lua +=== TEST 10: ngx.sleep available in log_by_lua --- config location /t { echo hello; - log_by_lua ' - ngx.sleep(0.1) - '; + log_by_lua_block { + ngx.sleep(0.001) + } } --- request GET /t --- response_body hello ---- wait: 0.1 --- error_log -API disabled in the context of log_by_lua* +lua ready to sleep for 1 ms @@ -500,3 +499,66 @@ f end worker cycle e?poll timer: 0 / + + + +=== TEST 18: ngx.sleep(0) in no-yieldable phases +--- config + location /t { + echo hello; + log_by_lua_block { + ngx.sleep(0) + } + } +--- request +GET /t +--- response_body +hello +--- error_log +lua ready to sleep for 0 ms + + + +=== TEST 19: ngx.sleep available in init_worker_by_lua +--- http_config + init_worker_by_lua_block { + local start = ngx.now() + ngx.sleep(0.1) + ngx.update_time() + package.loaded.gap = ngx.now() - start + } +--- config + location /t { + content_by_lua_block { + ngx.say(package.loaded.gap >= 0.1) + } + } +--- request +GET /t +--- no_error_log +[error] +--- response_body +true + + + +=== TEST 20: ngx.sleep available in init_by_lua +--- http_config + init_by_lua_block { + local start = ngx.now() + ngx.sleep(0.1) + ngx.update_time() + package.loaded.gap = ngx.now() - start + } +--- config + location /t { + content_by_lua_block { + ngx.say(package.loaded.gap >= 0.1) + } + } +--- request +GET /t +--- no_error_log +[error] +--- response_body +true