Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_darwin_sendfile_chain.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 
12 
13 /*
14  * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
15  * old bug as early FreeBSD sendfile() syscall:
16  * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
17  *
18  * Besides sendfile() has another bug: if one calls sendfile()
19  * with both a header and a trailer, then sendfile() ignores a file part
20  * at all and sends only the header and the trailer together.
21  * For this reason we send a trailer only if there is no a header.
22  *
23  * Although sendfile() allows to pass a header or a trailer,
24  * it may send the header or the trailer and a part of the file
25  * in different packets. And FreeBSD workaround (TCP_NOPUSH option)
26  * does not help.
27  */
28 
29 
30 #if (IOV_MAX > 64)
31 #define NGX_HEADERS 64
32 #define NGX_TRAILERS 64
33 #else
34 #define NGX_HEADERS IOV_MAX
35 #define NGX_TRAILERS IOV_MAX
36 #endif
37 
38 
41 {
42  int rc;
43  u_char *prev;
44  off_t size, send, prev_send, aligned, sent, fprev;
45  off_t header_size, file_size;
46  ngx_uint_t eintr, complete;
47  ngx_err_t err;
48  ngx_buf_t *file;
49  ngx_array_t header, trailer;
50  ngx_event_t *wev;
51  ngx_chain_t *cl;
52  struct sf_hdtr hdtr;
53  struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
54 
55  wev = c->write;
56 
57  if (!wev->ready) {
58  return in;
59  }
60 
61 #if (NGX_HAVE_KQUEUE)
62 
64  (void) ngx_connection_error(c, wev->kq_errno,
65  "kevent() reported about an closed connection");
66  wev->error = 1;
67  return NGX_CHAIN_ERROR;
68  }
69 
70 #endif
71 
72  /* the maximum limit size is the maximum size_t value - the page size */
73 
74  if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
76  }
77 
78  send = 0;
79 
80  header.elts = headers;
81  header.size = sizeof(struct iovec);
82  header.nalloc = NGX_HEADERS;
83  header.pool = c->pool;
84 
85  trailer.elts = trailers;
86  trailer.size = sizeof(struct iovec);
87  trailer.nalloc = NGX_TRAILERS;
88  trailer.pool = c->pool;
89 
90  for ( ;; ) {
91  file = NULL;
92  file_size = 0;
93  header_size = 0;
94  eintr = 0;
95  complete = 0;
96  prev_send = send;
97 
98  header.nelts = 0;
99  trailer.nelts = 0;
100 
101  /* create the header iovec and coalesce the neighbouring bufs */
102 
103  prev = NULL;
104  iov = NULL;
105 
106  for (cl = in; cl && send < limit; cl = cl->next) {
107 
108  if (ngx_buf_special(cl->buf)) {
109  continue;
110  }
111 
112  if (!ngx_buf_in_memory_only(cl->buf)) {
113  break;
114  }
115 
116  size = cl->buf->last - cl->buf->pos;
117 
118  if (send + size > limit) {
119  size = limit - send;
120  }
121 
122  if (prev == cl->buf->pos) {
123  iov->iov_len += (size_t) size;
124 
125  } else {
126  if (header.nelts >= IOV_MAX) {
127  break;
128  }
129 
130  iov = ngx_array_push(&header);
131  if (iov == NULL) {
132  return NGX_CHAIN_ERROR;
133  }
134 
135  iov->iov_base = (void *) cl->buf->pos;
136  iov->iov_len = (size_t) size;
137  }
138 
139  prev = cl->buf->pos + (size_t) size;
140  header_size += size;
141  send += size;
142  }
143 
144 
145  if (cl && cl->buf->in_file && send < limit) {
146  file = cl->buf;
147 
148  /* coalesce the neighbouring file bufs */
149 
150  do {
151  size = cl->buf->file_last - cl->buf->file_pos;
152 
153  if (send + size > limit) {
154  size = limit - send;
155 
156  aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
157  & ~((off_t) ngx_pagesize - 1);
158 
159  if (aligned <= cl->buf->file_last) {
160  size = aligned - cl->buf->file_pos;
161  }
162  }
163 
164  file_size += size;
165  send += size;
166  fprev = cl->buf->file_pos + size;
167  cl = cl->next;
168 
169  } while (cl
170  && cl->buf->in_file
171  && send < limit
172  && file->file->fd == cl->buf->file->fd
173  && fprev == cl->buf->file_pos);
174  }
175 
176  if (file && header.nelts == 0) {
177 
178  /* create the trailer iovec and coalesce the neighbouring bufs */
179 
180  prev = NULL;
181  iov = NULL;
182 
183  while (cl && send < limit) {
184 
185  if (ngx_buf_special(cl->buf)) {
186  cl = cl->next;
187  continue;
188  }
189 
190  if (!ngx_buf_in_memory_only(cl->buf)) {
191  break;
192  }
193 
194  size = cl->buf->last - cl->buf->pos;
195 
196  if (send + size > limit) {
197  size = limit - send;
198  }
199 
200  if (prev == cl->buf->pos) {
201  iov->iov_len += (size_t) size;
202 
203  } else {
204  if (trailer.nelts >= IOV_MAX) {
205  break;
206  }
207 
208  iov = ngx_array_push(&trailer);
209  if (iov == NULL) {
210  return NGX_CHAIN_ERROR;
211  }
212 
213  iov->iov_base = (void *) cl->buf->pos;
214  iov->iov_len = (size_t) size;
215  }
216 
217  prev = cl->buf->pos + (size_t) size;
218  send += size;
219  cl = cl->next;
220  }
221  }
222 
223  if (file) {
224 
225  /*
226  * sendfile() returns EINVAL if sf_hdtr's count is 0,
227  * but corresponding pointer is not NULL
228  */
229 
230  hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
231  hdtr.hdr_cnt = header.nelts;
232  hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
233  hdtr.trl_cnt = trailer.nelts;
234 
235  sent = header_size + file_size;
236 
238  "sendfile: @%O %O h:%O",
239  file->file_pos, sent, header_size);
240 
241  rc = sendfile(file->file->fd, c->fd, file->file_pos,
242  &sent, &hdtr, 0);
243 
244  if (rc == -1) {
245  err = ngx_errno;
246 
247  switch (err) {
248  case NGX_EAGAIN:
249  break;
250 
251  case NGX_EINTR:
252  eintr = 1;
253  break;
254 
255  default:
256  wev->error = 1;
257  (void) ngx_connection_error(c, err, "sendfile() failed");
258  return NGX_CHAIN_ERROR;
259  }
260 
262  "sendfile() sent only %O bytes", sent);
263  }
264 
265  if (rc == 0 && sent == 0) {
266 
267  /*
268  * if rc and sent equal to zero, then someone
269  * has truncated the file, so the offset became beyond
270  * the end of the file
271  */
272 
274  "sendfile() reported that \"%s\" was truncated",
275  file->file->name.data);
276 
277  return NGX_CHAIN_ERROR;
278  }
279 
281  "sendfile: %d, @%O %O:%O",
282  rc, file->file_pos, sent, file_size + header_size);
283 
284  } else {
285  rc = writev(c->fd, header.elts, header.nelts);
286 
288  "writev: %d of %uz", rc, send);
289 
290  if (rc == -1) {
291  err = ngx_errno;
292 
293  switch (err) {
294  case NGX_EAGAIN:
295  break;
296 
297  case NGX_EINTR:
298  eintr = 1;
299  break;
300 
301  default:
302  wev->error = 1;
303  ngx_connection_error(c, err, "writev() failed");
304  return NGX_CHAIN_ERROR;
305  }
306 
308  "writev() not ready");
309  }
310 
311  sent = rc > 0 ? rc : 0;
312  }
313 
314  if (send - prev_send == sent) {
315  complete = 1;
316  }
317 
318  c->sent += sent;
319 
320  for (cl = in; cl; cl = cl->next) {
321 
322  if (ngx_buf_special(cl->buf)) {
323  continue;
324  }
325 
326  if (sent == 0) {
327  break;
328  }
329 
330  size = ngx_buf_size(cl->buf);
331 
332  if (sent >= size) {
333  sent -= size;
334 
335  if (ngx_buf_in_memory(cl->buf)) {
336  cl->buf->pos = cl->buf->last;
337  }
338 
339  if (cl->buf->in_file) {
340  cl->buf->file_pos = cl->buf->file_last;
341  }
342 
343  continue;
344  }
345 
346  if (ngx_buf_in_memory(cl->buf)) {
347  cl->buf->pos += (size_t) sent;
348  }
349 
350  if (cl->buf->in_file) {
351  cl->buf->file_pos += sent;
352  }
353 
354  break;
355  }
356 
357  if (eintr) {
358  continue;
359  }
360 
361  if (!complete) {
362  wev->ready = 0;
363  return cl;
364  }
365 
366  if (send >= limit || cl == NULL) {
367  return cl;
368  }
369 
370  in = cl;
371  }
372 }