Annotation of embedaddon/nginx/src/os/unix/ngx_darwin_sendfile_chain.c, revision 1.1.1.1
1.1 misho 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:
39: ngx_chain_t *
40: ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
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:
63: if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
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)) {
75: limit = 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:
237: ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
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:
261: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
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:
273: ngx_log_error(NGX_LOG_ALERT, c->log, 0,
274: "sendfile() reported that \"%s\" was truncated",
275: file->file->name.data);
276:
277: return NGX_CHAIN_ERROR;
278: }
279:
280: ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
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:
287: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
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:
307: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>