From 2eb750144e6e6625fd9ad6d70ee9ebed7d285b73 Mon Sep 17 00:00:00 2001 From: kurt tu Date: Sun, 6 Sep 2026 14:29:22 +0800 Subject: [PATCH] feat: support cosocket and other yieldable APIs in init_worker_by_lua* Enable TCP/UDP cosockets, ngx.sleep, ngx.semaphore, ngx.thread.spawn, coroutine.*, and ngx.run_worker_thread inside init_worker_by_lua* by running a bounded event pump when the init code yields. Key changes: - Add ngx_http_lua_init_worker_pump() to drive the event loop during yields - Add ngx_http_lua_init_worker_pump_loop() with timeout budget management - Add ngx_http_lua_init_worker_toggle_accept() to disarm/re-arm listeners around the pump (prevents level-triggered busy-spin from pending accepts) - Freeze ngx.timer.at registrations via ctx->context gate; flush at runner exit with absolute expiry preservation (timer ordering identical to before) - Add lua_init_worker_timeout directive (default 0 = no timeout) - Add lua_init_worker_abort_on_error directive (default off) - Add INIT_WORKER to NGX_HTTP_LUA_CONTEXT_YIELDABLE mask in util.h - Change runtime error prefix to "lua entry thread aborted:" (breaking) - Update README: new directives, cosocket availability, 118 API context lines --- README.markdown | 171 +++-- src/ngx_http_lua_common.h | 4 + src/ngx_http_lua_initworkerby.c | 339 ++++++++- src/ngx_http_lua_module.c | 23 + src/ngx_http_lua_timer.c | 7 + src/ngx_http_lua_util.h | 3 +- t/194-init-worker-cosocket.t | 985 +++++++++++++++++++++++++++ t/195-init-worker-accept.t | 79 +++ t/196-init-worker-accept-workers.t | 73 ++ t/197-init-worker-accept-reuseport.t | 77 +++ t/198-init-worker-abort.t | 60 ++ t/199-init-worker-blocked.t | 187 +++++ 12 files changed, 1939 insertions(+), 69 deletions(-) create mode 100644 t/194-init-worker-cosocket.t create mode 100644 t/195-init-worker-accept.t create mode 100644 t/196-init-worker-accept-workers.t create mode 100644 t/197-init-worker-accept-reuseport.t create mode 100644 t/198-init-worker-abort.t create mode 100644 t/199-init-worker-blocked.t diff --git a/README.markdown b/README.markdown index 96c01ebba3..7ced5bdf6b 100644 --- a/README.markdown +++ b/README.markdown @@ -811,7 +811,9 @@ Cosockets Not Available Everywhere Due to internal limitations in the Nginx core, the cosocket API is disabled in the following contexts: [set_by_lua*](#set_by_lua), [log_by_lua*](#log_by_lua), [header_filter_by_lua*](#header_filter_by_lua), and [body_filter_by_lua](#body_filter_by_lua). -The cosockets are currently also disabled in the [init_by_lua*](#init_by_lua) and [init_worker_by_lua*](#init_worker_by_lua) directive contexts but we may add support for these contexts in the future because there is no limitation in the Nginx core (or the limitation might be worked around). +The cosockets are currently also disabled in the [init_by_lua*](#init_by_lua) directive contexts but we may add support for these contexts in the future because there is no limitation in the Nginx core (or the limitation might be worked around). + +Cosockets **are now supported** in the [init_worker_by_lua*](#init_worker_by_lua_block) directive contexts. When a cosocket operation yields (e.g. during a `connect` or `receive` call), the module runs a lightweight event pump that drives the event loop until the operation completes or the [lua_init_worker_timeout](#lua_init_worker_timeout) budget is exhausted. Note that `ngx.timer.at` callbacks registered in `init_worker_by_lua*` are deferred until the init code finishes running. There exists a workaround, however, when the original context does *not* need to wait for the cosocket results. That is, creating a zero-delay timer via the [ngx.timer.at](#ngxtimerat) API and do the cosocket results in the timer handler, which runs asynchronously as to the original context creating the timer. @@ -1190,6 +1192,8 @@ Directives * [lua_socket_pool_size](#lua_socket_pool_size) * [lua_socket_keepalive_timeout](#lua_socket_keepalive_timeout) * [lua_socket_log_errors](#lua_socket_log_errors) +* [lua_init_worker_timeout](#lua_init_worker_timeout) +* [lua_init_worker_abort_on_error](#lua_init_worker_abort_on_error) * [lua_ssl_ciphers](#lua_ssl_ciphers) * [lua_ssl_crl](#lua_ssl_crl) * [lua_ssl_protocols](#lua_ssl_protocols) @@ -1674,6 +1678,10 @@ This directive was first introduced in the `v0.9.17` release. This hook no longer runs in the cache manager and cache loader processes since the `v0.10.12` release. +Starting from this version, cosocket operations (e.g. `ngx.socket.tcp`, `ngx.socket.udp`, `ngx.sleep`, `ngx.semaphore`, `ngx.thread.spawn`) are supported in this context. When such an operation yields, the module runs a lightweight event pump to drive the Nginx event loop until the operation completes. Use [lua_init_worker_timeout](#lua_init_worker_timeout) to bound how long the init code can block worker startup, and [lua_init_worker_abort_on_error](#lua_init_worker_abort_on_error) to control whether a Lua runtime error should abort the worker process. + +Note that `ngx.timer.at` callbacks registered during this hook are deferred: they will not run until the init code finishes. This preserves the implicit ordering guarantee that timer callbacks registered in `init_worker_by_lua*` execute only after the init code completes. + [Back to TOC](#directives) init_worker_by_lua_file @@ -3577,6 +3585,49 @@ This directive was first introduced in the `v0.5.13` release. [Back to TOC](#directives) +lua_init_worker_timeout +----------------------- + +**syntax:** *lua_init_worker_timeout <time>* + +**default:** *lua_init_worker_timeout 0* + +**context:** *http* + +Sets the maximum wall-clock time that `init_worker_by_lua*` code is allowed to block worker startup. When the timeout is reached, the Lua code is forcibly aborted and the worker continues starting normally (the error is logged at the `ERR` level). + +The default value `0` means no timeout: a hung remote connection will block the worker indefinitely. + +The `