Mercurial > hg > nginx
comparison src/http/ngx_http_copy_filter_module.c @ 7974:555533169506
HTTP/2: fixed "task already active" with sendfile in threads.
With sendfile in threads, "task already active" alerts might appear in logs
if a write event happens on the main HTTP/2 connection, triggering a sendfile
in threads while another thread operation is already running. Observed
with "aio threads; aio_write on; sendfile on;" and with thread event handlers
modified to post a write event to the main HTTP/2 connection (though can
happen without any modifications).
Similarly, sendfile() with AIO preloading on FreeBSD can trigger duplicate
aio operation, resulting in "second aio post" alerts. This is, however,
harder to reproduce, especially on modern FreeBSD systems, since sendfile()
usually does not return EBUSY.
Fix is to avoid starting a sendfile operation if other thread operation
is active by checking r->aio in the thread handler (and, similarly, in
aio preload handler). The added check also makes duplicate calls protection
redundant, so it is removed.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Thu, 25 Nov 2021 22:02:05 +0300 |
parents | ce37362a7a70 |
children | a7a77549265e |
comparison
equal
deleted
inserted
replaced
7973:3443c02ca1d1 | 7974:555533169506 |
---|---|
217 static u_char buf[1]; | 217 static u_char buf[1]; |
218 ngx_event_aio_t *aio; | 218 ngx_event_aio_t *aio; |
219 ngx_http_request_t *r; | 219 ngx_http_request_t *r; |
220 ngx_output_chain_ctx_t *ctx; | 220 ngx_output_chain_ctx_t *ctx; |
221 | 221 |
222 aio = file->file->aio; | |
223 r = aio->data; | |
224 | |
225 if (r->aio) { | |
226 /* | |
227 * tolerate sendfile() calls if another operation is already | |
228 * running; this can happen due to subrequests, multiple calls | |
229 * of the next body filter from a filter, or in HTTP/2 due to | |
230 * a write event on the main connection | |
231 */ | |
232 | |
233 return NGX_AGAIN; | |
234 } | |
235 | |
222 n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL); | 236 n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL); |
223 | 237 |
224 if (n == NGX_AGAIN) { | 238 if (n == NGX_AGAIN) { |
225 aio = file->file->aio; | |
226 aio->handler = ngx_http_copy_aio_sendfile_event_handler; | 239 aio->handler = ngx_http_copy_aio_sendfile_event_handler; |
227 | 240 |
228 r = aio->data; | |
229 r->main->blocked++; | 241 r->main->blocked++; |
230 r->aio = 1; | 242 r->aio = 1; |
231 | 243 |
232 ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module); | 244 ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module); |
233 ctx->aio = 1; | 245 ctx->aio = 1; |
261 | 273 |
262 static ngx_int_t | 274 static ngx_int_t |
263 ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file) | 275 ngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file) |
264 { | 276 { |
265 ngx_str_t name; | 277 ngx_str_t name; |
278 ngx_connection_t *c; | |
266 ngx_thread_pool_t *tp; | 279 ngx_thread_pool_t *tp; |
267 ngx_http_request_t *r; | 280 ngx_http_request_t *r; |
268 ngx_output_chain_ctx_t *ctx; | 281 ngx_output_chain_ctx_t *ctx; |
269 ngx_http_core_loc_conf_t *clcf; | 282 ngx_http_core_loc_conf_t *clcf; |
270 | 283 |
271 r = file->thread_ctx; | 284 r = file->thread_ctx; |
285 | |
286 if (r->aio) { | |
287 /* | |
288 * tolerate sendfile() calls if another operation is already | |
289 * running; this can happen due to subrequests, multiple calls | |
290 * of the next body filter from a filter, or in HTTP/2 due to | |
291 * a write event on the main connection | |
292 */ | |
293 | |
294 c = r->connection; | |
295 | |
296 #if (NGX_HTTP_V2) | |
297 if (r->stream) { | |
298 c = r->stream->connection->connection; | |
299 } | |
300 #endif | |
301 | |
302 if (task == c->sendfile_task) { | |
303 return NGX_OK; | |
304 } | |
305 } | |
272 | 306 |
273 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | 307 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
274 tp = clcf->thread_pool; | 308 tp = clcf->thread_pool; |
275 | 309 |
276 if (tp == NULL) { | 310 if (tp == NULL) { |