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) {