# HG changeset patch # User Maxim Dounin # Date 1490714139 -10800 # Node ID ff0c8e11edbc590ba3b6609aa6b8ac2260580775 # Parent 3fb9b5eb75c085e4c4b74c2aa0983a62aed85d54 Simplified and improved sendfile() code on Linux. The ngx_linux_sendfile() function is now used for both normal sendfile() and sendfile in threads. The ngx_linux_sendfile_thread() function was modified to use the same interface as ngx_linux_sendfile(), and is simply called from ngx_linux_sendfile() when threads are enabled. Special return code NGX_DONE is used to indicate that a thread task was posted and no further actions are needed. If number of bytes sent is less that what we were sending, we now always retry sending. This is needed for sendfile() in threads as the number of bytes we are sending might have been changed since the thread task was posted. And this is also needed for Linux 4.3+, as sendfile() might be interrupted at any time and provides no indication if it was interrupted or not (ticket #1174). diff --git a/src/os/unix/ngx_linux_sendfile_chain.c b/src/os/unix/ngx_linux_sendfile_chain.c --- a/src/os/unix/ngx_linux_sendfile_chain.c +++ b/src/os/unix/ngx_linux_sendfile_chain.c @@ -20,8 +20,8 @@ static ssize_t ngx_linux_sendfile(ngx_co #error sendfile64() is required! #endif -static ngx_int_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, - size_t size, size_t *sent); +static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, + size_t size); static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); #endif @@ -56,10 +56,6 @@ ngx_linux_sendfile_chain(ngx_connection_ ngx_chain_t *cl; ngx_iovec_t header; struct iovec headers[NGX_IOVS_PREALLOCATE]; -#if (NGX_THREADS) - ngx_int_t rc; - ngx_uint_t thread_handled, thread_complete; -#endif wev = c->write; @@ -82,10 +78,6 @@ ngx_linux_sendfile_chain(ngx_connection_ for ( ;; ) { prev_send = send; -#if (NGX_THREADS) - thread_handled = 0; - thread_complete = 0; -#endif /* create the iovec and coalesce the neighbouring bufs */ @@ -179,37 +171,18 @@ ngx_linux_sendfile_chain(ngx_connection_ } #endif -#if (NGX_THREADS) - if (file->file->thread_handler) { - rc = ngx_linux_sendfile_thread(c, file, file_size, &sent); + n = ngx_linux_sendfile(c, file, file_size); - switch (rc) { - case NGX_OK: - thread_handled = 1; - break; - - case NGX_DONE: - thread_complete = 1; - break; - - case NGX_AGAIN: - break; + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } - default: /* NGX_ERROR */ - return NGX_CHAIN_ERROR; - } + if (n == NGX_DONE) { + /* thread task posted */ + return in; + } - } else -#endif - { - n = ngx_linux_sendfile(c, file, file_size); - - if (n == NGX_ERROR) { - return NGX_CHAIN_ERROR; - } - - sent = (n == NGX_AGAIN) ? 0 : n; - } + sent = (n == NGX_AGAIN) ? 0 : n; } else { n = ngx_writev(c, &header); @@ -225,21 +198,27 @@ ngx_linux_sendfile_chain(ngx_connection_ in = ngx_chain_update_sent(in, sent); - if ((size_t) (send - prev_send) != sent) { -#if (NGX_THREADS) - if (thread_handled) { - return in; - } - - if (thread_complete) { - send = prev_send + sent; - continue; - } -#endif + if (n == NGX_AGAIN) { wev->ready = 0; return in; } + if ((size_t) (send - prev_send) != sent) { + + /* + * sendfile() on Linux 4.3+ might be interrupted at any time, + * and provides no indication if it was interrupted or not, + * so we have to retry till an explicit EAGAIN + * + * sendfile() in threads can also report less bytes written + * than we are prepared to send now, since it was started in + * some point in the past, so we again have to retry + */ + + send = prev_send + sent; + continue; + } + if (send >= limit || in == NULL) { return in; } @@ -258,6 +237,14 @@ ngx_linux_sendfile(ngx_connection_t *c, ssize_t n; ngx_err_t err; +#if (NGX_THREADS) + + if (file->file->thread_handler) { + return ngx_linux_sendfile_thread(c, file, size); + } + +#endif + #if (NGX_HAVE_SENDFILE64) offset = file->file_pos; #else @@ -324,9 +311,8 @@ typedef struct { } ngx_linux_sendfile_ctx_t; -static ngx_int_t -ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, - size_t *sent) +static ssize_t +ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size) { ngx_event_t *wev; ngx_thread_task_t *task; @@ -356,10 +342,14 @@ ngx_linux_sendfile_thread(ngx_connection task->event.complete = 0; if (ctx->err == NGX_EAGAIN) { - *sent = 0; + /* + * if wev->complete is set, this means that a write event + * happened while we were waiting for the thread task, so + * we have to retry sending even on EAGAIN + */ if (wev->complete) { - return NGX_DONE; + return 0; } return NGX_AGAIN; @@ -384,13 +374,7 @@ ngx_linux_sendfile_thread(ngx_connection return NGX_ERROR; } - *sent = ctx->sent; - - if (ctx->sent == ctx->size || wev->complete) { - return NGX_DONE; - } - - return NGX_AGAIN; + return ctx->sent; } if (task->event.active && ctx->file == file) { @@ -399,9 +383,7 @@ ngx_linux_sendfile_thread(ngx_connection * or multiple calls of the next body filter from a filter */ - *sent = 0; - - return NGX_OK; + return NGX_DONE; } ctx->file = file; @@ -414,9 +396,7 @@ ngx_linux_sendfile_thread(ngx_connection return NGX_ERROR; } - *sent = 0; - - return NGX_OK; + return NGX_DONE; }