Klaus Demo nginx / ff33d9f
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). Maxim Dounin 3 years ago
1 changed file(s) with 50 addition(s) and 70 deletion(s). Raw diff Collapse all Expand all
1919 #error sendfile64() is required!
2020 #endif
2121
22 static ngx_int_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file,
23 size_t size, size_t *sent);
22 static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file,
23 size_t size);
2424 static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log);
2525 #endif
2626
5555 ngx_chain_t *cl;
5656 ngx_iovec_t header;
5757 struct iovec headers[NGX_IOVS_PREALLOCATE];
58 #if (NGX_THREADS)
59 ngx_int_t rc;
60 ngx_uint_t thread_handled, thread_complete;
61 #endif
6258
6359 wev = c->write;
6460
8177
8278 for ( ;; ) {
8379 prev_send = send;
84 #if (NGX_THREADS)
85 thread_handled = 0;
86 thread_complete = 0;
87 #endif
8880
8981 /* create the iovec and coalesce the neighbouring bufs */
9082
178170 }
179171 #endif
180172
181 #if (NGX_THREADS)
182 if (file->file->thread_handler) {
183 rc = ngx_linux_sendfile_thread(c, file, file_size, &sent);
184
185 switch (rc) {
186 case NGX_OK:
187 thread_handled = 1;
188 break;
189
190 case NGX_DONE:
191 thread_complete = 1;
192 break;
193
194 case NGX_AGAIN:
195 break;
196
197 default: /* NGX_ERROR */
198 return NGX_CHAIN_ERROR;
199 }
200
201 } else
202 #endif
203 {
204 n = ngx_linux_sendfile(c, file, file_size);
205
206 if (n == NGX_ERROR) {
207 return NGX_CHAIN_ERROR;
208 }
209
210 sent = (n == NGX_AGAIN) ? 0 : n;
211 }
173 n = ngx_linux_sendfile(c, file, file_size);
174
175 if (n == NGX_ERROR) {
176 return NGX_CHAIN_ERROR;
177 }
178
179 if (n == NGX_DONE) {
180 /* thread task posted */
181 return in;
182 }
183
184 sent = (n == NGX_AGAIN) ? 0 : n;
212185
213186 } else {
214187 n = ngx_writev(c, &header);
224197
225198 in = ngx_chain_update_sent(in, sent);
226199
227 if ((size_t) (send - prev_send) != sent) {
228 #if (NGX_THREADS)
229 if (thread_handled) {
230 return in;
231 }
232
233 if (thread_complete) {
234 send = prev_send + sent;
235 continue;
236 }
237 #endif
200 if (n == NGX_AGAIN) {
238201 wev->ready = 0;
239202 return in;
203 }
204
205 if ((size_t) (send - prev_send) != sent) {
206
207 /*
208 * sendfile() on Linux 4.3+ might be interrupted at any time,
209 * and provides no indication if it was interrupted or not,
210 * so we have to retry till an explicit EAGAIN
211 *
212 * sendfile() in threads can also report less bytes written
213 * than we are prepared to send now, since it was started in
214 * some point in the past, so we again have to retry
215 */
216
217 send = prev_send + sent;
218 continue;
240219 }
241220
242221 if (send >= limit || in == NULL) {
257236 ssize_t n;
258237 ngx_err_t err;
259238
239 #if (NGX_THREADS)
240
241 if (file->file->thread_handler) {
242 return ngx_linux_sendfile_thread(c, file, size);
243 }
244
245 #endif
246
260247 #if (NGX_HAVE_SENDFILE64)
261248 offset = file->file_pos;
262249 #else
323310 } ngx_linux_sendfile_ctx_t;
324311
325312
326 static ngx_int_t
327 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size,
328 size_t *sent)
313 static ssize_t
314 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size)
329315 {
330316 ngx_event_t *wev;
331317 ngx_thread_task_t *task;
355341 task->event.complete = 0;
356342
357343 if (ctx->err == NGX_EAGAIN) {
358 *sent = 0;
344 /*
345 * if wev->complete is set, this means that a write event
346 * happened while we were waiting for the thread task, so
347 * we have to retry sending even on EAGAIN
348 */
359349
360350 if (wev->complete) {
361 return NGX_DONE;
351 return 0;
362352 }
363353
364354 return NGX_AGAIN;
383373 return NGX_ERROR;
384374 }
385375
386 *sent = ctx->sent;
387
388 if (ctx->sent == ctx->size || wev->complete) {
389 return NGX_DONE;
390 }
391
392 return NGX_AGAIN;
376 return ctx->sent;
393377 }
394378
395379 if (task->event.active && ctx->file == file) {
398382 * or multiple calls of the next body filter from a filter
399383 */
400384
401 *sent = 0;
402
403 return NGX_OK;
385 return NGX_DONE;
404386 }
405387
406388 ctx->file = file;
413395 return NGX_ERROR;
414396 }
415397
416 *sent = 0;
417
418 return NGX_OK;
398 return NGX_DONE;
419399 }
420400
421401