Mercurial > hg > nginx
comparison src/os/unix/ngx_linux_sendfile_chain.c @ 6949:ff0c8e11edbc
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).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Tue, 28 Mar 2017 18:15:39 +0300 |
parents | f01ab2dbcfdc |
children | 400a3412b1e3 |
comparison
equal
deleted
inserted
replaced
6948:3fb9b5eb75c0 | 6949:ff0c8e11edbc |
---|---|
18 | 18 |
19 #if !(NGX_HAVE_SENDFILE64) | 19 #if !(NGX_HAVE_SENDFILE64) |
20 #error sendfile64() is required! | 20 #error sendfile64() is required! |
21 #endif | 21 #endif |
22 | 22 |
23 static ngx_int_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, | 23 static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, |
24 size_t size, size_t *sent); | 24 size_t size); |
25 static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); | 25 static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); |
26 #endif | 26 #endif |
27 | 27 |
28 | 28 |
29 /* | 29 /* |
54 ngx_buf_t *file; | 54 ngx_buf_t *file; |
55 ngx_event_t *wev; | 55 ngx_event_t *wev; |
56 ngx_chain_t *cl; | 56 ngx_chain_t *cl; |
57 ngx_iovec_t header; | 57 ngx_iovec_t header; |
58 struct iovec headers[NGX_IOVS_PREALLOCATE]; | 58 struct iovec headers[NGX_IOVS_PREALLOCATE]; |
59 #if (NGX_THREADS) | |
60 ngx_int_t rc; | |
61 ngx_uint_t thread_handled, thread_complete; | |
62 #endif | |
63 | 59 |
64 wev = c->write; | 60 wev = c->write; |
65 | 61 |
66 if (!wev->ready) { | 62 if (!wev->ready) { |
67 return in; | 63 return in; |
80 header.iovs = headers; | 76 header.iovs = headers; |
81 header.nalloc = NGX_IOVS_PREALLOCATE; | 77 header.nalloc = NGX_IOVS_PREALLOCATE; |
82 | 78 |
83 for ( ;; ) { | 79 for ( ;; ) { |
84 prev_send = send; | 80 prev_send = send; |
85 #if (NGX_THREADS) | |
86 thread_handled = 0; | |
87 thread_complete = 0; | |
88 #endif | |
89 | 81 |
90 /* create the iovec and coalesce the neighbouring bufs */ | 82 /* create the iovec and coalesce the neighbouring bufs */ |
91 | 83 |
92 cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); | 84 cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); |
93 | 85 |
177 ngx_debug_point(); | 169 ngx_debug_point(); |
178 return NGX_CHAIN_ERROR; | 170 return NGX_CHAIN_ERROR; |
179 } | 171 } |
180 #endif | 172 #endif |
181 | 173 |
182 #if (NGX_THREADS) | 174 n = ngx_linux_sendfile(c, file, file_size); |
183 if (file->file->thread_handler) { | 175 |
184 rc = ngx_linux_sendfile_thread(c, file, file_size, &sent); | 176 if (n == NGX_ERROR) { |
185 | 177 return NGX_CHAIN_ERROR; |
186 switch (rc) { | 178 } |
187 case NGX_OK: | 179 |
188 thread_handled = 1; | 180 if (n == NGX_DONE) { |
189 break; | 181 /* thread task posted */ |
190 | 182 return in; |
191 case NGX_DONE: | 183 } |
192 thread_complete = 1; | 184 |
193 break; | 185 sent = (n == NGX_AGAIN) ? 0 : n; |
194 | |
195 case NGX_AGAIN: | |
196 break; | |
197 | |
198 default: /* NGX_ERROR */ | |
199 return NGX_CHAIN_ERROR; | |
200 } | |
201 | |
202 } else | |
203 #endif | |
204 { | |
205 n = ngx_linux_sendfile(c, file, file_size); | |
206 | |
207 if (n == NGX_ERROR) { | |
208 return NGX_CHAIN_ERROR; | |
209 } | |
210 | |
211 sent = (n == NGX_AGAIN) ? 0 : n; | |
212 } | |
213 | 186 |
214 } else { | 187 } else { |
215 n = ngx_writev(c, &header); | 188 n = ngx_writev(c, &header); |
216 | 189 |
217 if (n == NGX_ERROR) { | 190 if (n == NGX_ERROR) { |
223 | 196 |
224 c->sent += sent; | 197 c->sent += sent; |
225 | 198 |
226 in = ngx_chain_update_sent(in, sent); | 199 in = ngx_chain_update_sent(in, sent); |
227 | 200 |
228 if ((size_t) (send - prev_send) != sent) { | 201 if (n == NGX_AGAIN) { |
229 #if (NGX_THREADS) | |
230 if (thread_handled) { | |
231 return in; | |
232 } | |
233 | |
234 if (thread_complete) { | |
235 send = prev_send + sent; | |
236 continue; | |
237 } | |
238 #endif | |
239 wev->ready = 0; | 202 wev->ready = 0; |
240 return in; | 203 return in; |
204 } | |
205 | |
206 if ((size_t) (send - prev_send) != sent) { | |
207 | |
208 /* | |
209 * sendfile() on Linux 4.3+ might be interrupted at any time, | |
210 * and provides no indication if it was interrupted or not, | |
211 * so we have to retry till an explicit EAGAIN | |
212 * | |
213 * sendfile() in threads can also report less bytes written | |
214 * than we are prepared to send now, since it was started in | |
215 * some point in the past, so we again have to retry | |
216 */ | |
217 | |
218 send = prev_send + sent; | |
219 continue; | |
241 } | 220 } |
242 | 221 |
243 if (send >= limit || in == NULL) { | 222 if (send >= limit || in == NULL) { |
244 return in; | 223 return in; |
245 } | 224 } |
256 int32_t offset; | 235 int32_t offset; |
257 #endif | 236 #endif |
258 ssize_t n; | 237 ssize_t n; |
259 ngx_err_t err; | 238 ngx_err_t err; |
260 | 239 |
240 #if (NGX_THREADS) | |
241 | |
242 if (file->file->thread_handler) { | |
243 return ngx_linux_sendfile_thread(c, file, size); | |
244 } | |
245 | |
246 #endif | |
247 | |
261 #if (NGX_HAVE_SENDFILE64) | 248 #if (NGX_HAVE_SENDFILE64) |
262 offset = file->file_pos; | 249 offset = file->file_pos; |
263 #else | 250 #else |
264 offset = (int32_t) file->file_pos; | 251 offset = (int32_t) file->file_pos; |
265 #endif | 252 #endif |
322 size_t sent; | 309 size_t sent; |
323 ngx_err_t err; | 310 ngx_err_t err; |
324 } ngx_linux_sendfile_ctx_t; | 311 } ngx_linux_sendfile_ctx_t; |
325 | 312 |
326 | 313 |
327 static ngx_int_t | 314 static ssize_t |
328 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size, | 315 ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size) |
329 size_t *sent) | |
330 { | 316 { |
331 ngx_event_t *wev; | 317 ngx_event_t *wev; |
332 ngx_thread_task_t *task; | 318 ngx_thread_task_t *task; |
333 ngx_linux_sendfile_ctx_t *ctx; | 319 ngx_linux_sendfile_ctx_t *ctx; |
334 | 320 |
354 | 340 |
355 if (task->event.complete) { | 341 if (task->event.complete) { |
356 task->event.complete = 0; | 342 task->event.complete = 0; |
357 | 343 |
358 if (ctx->err == NGX_EAGAIN) { | 344 if (ctx->err == NGX_EAGAIN) { |
359 *sent = 0; | 345 /* |
346 * if wev->complete is set, this means that a write event | |
347 * happened while we were waiting for the thread task, so | |
348 * we have to retry sending even on EAGAIN | |
349 */ | |
360 | 350 |
361 if (wev->complete) { | 351 if (wev->complete) { |
362 return NGX_DONE; | 352 return 0; |
363 } | 353 } |
364 | 354 |
365 return NGX_AGAIN; | 355 return NGX_AGAIN; |
366 } | 356 } |
367 | 357 |
382 file->file->name.data, file->file_pos); | 372 file->file->name.data, file->file_pos); |
383 | 373 |
384 return NGX_ERROR; | 374 return NGX_ERROR; |
385 } | 375 } |
386 | 376 |
387 *sent = ctx->sent; | 377 return ctx->sent; |
388 | |
389 if (ctx->sent == ctx->size || wev->complete) { | |
390 return NGX_DONE; | |
391 } | |
392 | |
393 return NGX_AGAIN; | |
394 } | 378 } |
395 | 379 |
396 if (task->event.active && ctx->file == file) { | 380 if (task->event.active && ctx->file == file) { |
397 /* | 381 /* |
398 * tolerate duplicate calls; they can happen due to subrequests | 382 * tolerate duplicate calls; they can happen due to subrequests |
399 * or multiple calls of the next body filter from a filter | 383 * or multiple calls of the next body filter from a filter |
400 */ | 384 */ |
401 | 385 |
402 *sent = 0; | 386 return NGX_DONE; |
403 | |
404 return NGX_OK; | |
405 } | 387 } |
406 | 388 |
407 ctx->file = file; | 389 ctx->file = file; |
408 ctx->socket = c->fd; | 390 ctx->socket = c->fd; |
409 ctx->size = size; | 391 ctx->size = size; |
412 | 394 |
413 if (file->file->thread_handler(task, file->file) != NGX_OK) { | 395 if (file->file->thread_handler(task, file->file) != NGX_OK) { |
414 return NGX_ERROR; | 396 return NGX_ERROR; |
415 } | 397 } |
416 | 398 |
417 *sent = 0; | 399 return NGX_DONE; |
418 | |
419 return NGX_OK; | |
420 } | 400 } |
421 | 401 |
422 | 402 |
423 static void | 403 static void |
424 ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log) | 404 ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log) |