comparison src/os/unix/ngx_linux_sendfile_chain.c @ 6440:248aa2757332

Fixed timeouts with threaded sendfile() and subrequests. If a write event happens after sendfile() but before we've got the sendfile results in the main thread, this write event will be ignored. And if no more events will happen, the connection will hang. Removing the events works in the simple cases, but not always, as in some cases events are added back by an unrelated code. E.g., the upstream module adds write event in the ngx_http_upstream_init() to track client aborts. Fix is to use wev->complete instead. It is now set to 0 before a sendfile() task is posted, and it is set to 1 once a write event happens. If on completion of the sendfile() task wev->complete is 1, we know that an event happened while we were executing sendfile(), and the socket is still ready for writing even if sendfile() did not sent all the data or returned EAGAIN.
author Maxim Dounin <mdounin@mdounin.ru>
date Fri, 18 Mar 2016 05:04:45 +0300
parents 4df3d9fcdee8
children f01ab2dbcfdc
comparison
equal deleted inserted replaced
6439:81329f6a4254 6440:248aa2757332
326 326
327 static ngx_int_t 327 static ngx_int_t
328 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, 328 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size,
329 size_t *sent) 329 size_t *sent)
330 { 330 {
331 ngx_uint_t flags;
332 ngx_event_t *wev; 331 ngx_event_t *wev;
333 ngx_thread_task_t *task; 332 ngx_thread_task_t *task;
334 ngx_linux_sendfile_ctx_t *ctx; 333 ngx_linux_sendfile_ctx_t *ctx;
335 334
336 ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, 335 ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0,
356 if (task->event.complete) { 355 if (task->event.complete) {
357 task->event.complete = 0; 356 task->event.complete = 0;
358 357
359 if (ctx->err == NGX_EAGAIN) { 358 if (ctx->err == NGX_EAGAIN) {
360 *sent = 0; 359 *sent = 0;
360
361 if (wev->complete) {
362 return NGX_DONE;
363 }
364
361 return NGX_AGAIN; 365 return NGX_AGAIN;
362 } 366 }
363 367
364 if (ctx->err) { 368 if (ctx->err) {
365 wev->error = 1; 369 wev->error = 1;
380 return NGX_ERROR; 384 return NGX_ERROR;
381 } 385 }
382 386
383 *sent = ctx->sent; 387 *sent = ctx->sent;
384 388
385 return (ctx->sent == ctx->size) ? NGX_DONE : NGX_AGAIN; 389 if (ctx->sent == ctx->size || wev->complete) {
390 return NGX_DONE;
391 }
392
393 return NGX_AGAIN;
386 } 394 }
387 395
388 if (task->event.active && ctx->file == file) { 396 if (task->event.active && ctx->file == file) {
389 /* 397 /*
390 * tolerate duplicate calls; they can happen due to subrequests 398 * tolerate duplicate calls; they can happen due to subrequests
398 406
399 ctx->file = file; 407 ctx->file = file;
400 ctx->socket = c->fd; 408 ctx->socket = c->fd;
401 ctx->size = size; 409 ctx->size = size;
402 410
403 if (wev->active) { 411 wev->complete = 0;
404 flags = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? NGX_CLEAR_EVENT
405 : NGX_LEVEL_EVENT;
406
407 if (ngx_del_event(wev, NGX_WRITE_EVENT, flags) == NGX_ERROR) {
408 return NGX_ERROR;
409 }
410 }
411 412
412 if (file->file->thread_handler(task, file->file) != NGX_OK) { 413 if (file->file->thread_handler(task, file->file) != NGX_OK) {
413 return NGX_ERROR; 414 return NGX_ERROR;
414 } 415 }
415 416