[mod_evhost] fix an incorrect error trace
[lighttpd.git] / src / network_write_mmap.c
blob83add9428733bec35440b83a5b2a3799c4244883
1 #include "first.h"
3 #include "network_backends.h"
5 #include "network.h"
6 #include "log.h"
7 #include "sys-mmap.h"
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <unistd.h>
13 #include <errno.h>
14 #include <string.h>
16 #define MMAP_CHUNK_SIZE (512*1024)
18 off_t mmap_align_offset(off_t start) {
19 static long pagesize = 0;
20 if (0 == pagesize) {
21 pagesize = sysconf(_SC_PAGESIZE);
22 force_assert(pagesize < MMAP_CHUNK_SIZE);
24 force_assert(start >= (start % pagesize));
25 return start - (start % pagesize);
28 #if defined(USE_MMAP)
30 static volatile int sigbus_jmp_valid;
31 static sigjmp_buf sigbus_jmp;
33 static void sigbus_handler(int sig) {
34 UNUSED(sig);
35 if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
36 log_failed_assert(__FILE__, __LINE__, "SIGBUS");
39 #if 0
40 /* read mmap()ed data into local buffer */
41 #define LOCAL_BUFFERING 1
42 #endif
44 int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
45 chunk* const c = cq->first;
46 off_t offset, toSend, file_end;
47 ssize_t r;
48 size_t mmap_offset, mmap_avail;
49 const char *data;
51 force_assert(NULL != c);
52 force_assert(FILE_CHUNK == c->type);
53 force_assert(c->offset >= 0 && c->offset <= c->file.length);
55 offset = c->file.start + c->offset;
56 toSend = c->file.length - c->offset;
57 if (toSend > *p_max_bytes) toSend = *p_max_bytes;
58 file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
60 if (0 == toSend) {
61 chunkqueue_remove_finished_chunks(cq);
62 return 0;
65 if (0 != network_open_file_chunk(srv, con, cq)) return -1;
67 /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
68 if (0 != sigsetjmp(sigbus_jmp, 1)) {
69 sigbus_jmp_valid = 0;
71 log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:",
72 c->file.name, c->file.fd);
74 munmap(c->file.mmap.start, c->file.mmap.length);
75 c->file.mmap.start = MAP_FAILED;
76 #ifdef LOCAL_BUFFERING
77 buffer_reset(c->mem);
78 #endif
80 return -1;
83 signal(SIGBUS, sigbus_handler);
85 /* mmap the buffer if offset is outside old mmap area or not mapped at all */
86 if (MAP_FAILED == c->file.mmap.start
87 || offset < c->file.mmap.offset
88 || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
90 if (MAP_FAILED != c->file.mmap.start) {
91 munmap(c->file.mmap.start, c->file.mmap.length);
92 c->file.mmap.start = MAP_FAILED;
95 /* Optimizations for the future:
97 * adaptive mem-mapping
98 * the problem:
99 * we mmap() the whole file. If someone has alot large files and 32bit
100 * machine the virtual address area will be unrun and we will have a failing
101 * mmap() call.
102 * solution:
103 * only mmap 16M in one chunk and move the window as soon as we have finished
104 * the first 8M
106 * read-ahead buffering
107 * the problem:
108 * sending out several large files in parallel trashes the read-ahead of the
109 * kernel leading to long wait-for-seek times.
110 * solutions: (increasing complexity)
111 * 1. use madvise
112 * 2. use a internal read-ahead buffer in the chunk-structure
113 * 3. use non-blocking IO for file-transfers
114 * */
116 c->file.mmap.offset = mmap_align_offset(offset);
118 /* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */
119 c->file.mmap.length = MMAP_CHUNK_SIZE;
120 if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
121 c->file.mmap.length = file_end - c->file.mmap.offset;
124 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) {
125 log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
126 strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
127 return -1;
130 #if defined(LOCAL_BUFFERING)
131 sigbus_jmp_valid = 1;
132 buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length);
133 sigbus_jmp_valid = 0;
134 #else
135 # if defined(HAVE_MADVISE)
136 /* don't advise files < 64Kb */
137 if (c->file.mmap.length > (64*1024)) {
138 /* darwin 7 is returning EINVAL all the time and I don't know how to
139 * detect this at runtime.
141 * ignore the return value for now */
142 madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
144 # endif
145 #endif
148 force_assert(offset >= c->file.mmap.offset);
149 mmap_offset = offset - c->file.mmap.offset;
150 force_assert(c->file.mmap.length > mmap_offset);
151 mmap_avail = c->file.mmap.length - mmap_offset;
152 if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
154 #if defined(LOCAL_BUFFERING)
155 data = c->mem->ptr + mmap_offset;
156 #else
157 data = c->file.mmap.start + mmap_offset;
158 #endif
160 sigbus_jmp_valid = 1;
161 #if defined(__WIN32)
162 r = send(fd, data, toSend, 0);
163 #else /* __WIN32 */
164 r = write(fd, data, toSend);
165 #endif /* __WIN32 */
166 sigbus_jmp_valid = 0;
168 #if defined(__WIN32)
169 if (r < 0) {
170 int lastError = WSAGetLastError();
171 switch (lastError) {
172 case WSAEINTR:
173 case WSAEWOULDBLOCK:
174 break;
175 case WSAECONNRESET:
176 case WSAETIMEDOUT:
177 case WSAECONNABORTED:
178 return -2;
179 default:
180 log_error_write(srv, __FILE__, __LINE__, "sdd",
181 "send failed: ", lastError, fd);
182 return -1;
185 #else /* __WIN32 */
186 if (r < 0) {
187 switch (errno) {
188 case EAGAIN:
189 case EINTR:
190 break;
191 case EPIPE:
192 case ECONNRESET:
193 return -2;
194 default:
195 log_error_write(srv, __FILE__, __LINE__, "ssd",
196 "write failed:", strerror(errno), fd);
197 return -1;
200 #endif /* __WIN32 */
202 if (r >= 0) {
203 *p_max_bytes -= r;
204 chunkqueue_mark_written(cq, r);
207 return (r > 0 && r == toSend) ? 0 : -3;
210 #endif /* USE_MMAP */