summaryrefslogtreecommitdiff
path: root/src/network_write_mmap.c
diff options
context:
space:
mode:
authorStefan Bühler <stbuehler@web.de>2015-08-22 16:00:59 +0000
committerStefan Bühler <stbuehler@web.de>2015-08-22 16:00:59 +0000
commit593599f14acf2ddbe31b24d970266d9e5fcab9c2 (patch)
treefe02b73a8313e80c58daf40d5653455a08cfb2c1 /src/network_write_mmap.c
parentfa8b154628faa4ce234d83f99cd9cb64e9a673c3 (diff)
downloadlighttpd1.4-593599f14acf2ddbe31b24d970266d9e5fcab9c2.tar.gz
lighttpd1.4-593599f14acf2ddbe31b24d970266d9e5fcab9c2.zip
rewrite network (write) backends
From: Stefan Bühler <stbuehler@web.de> git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@3012 152afb58-edef-0310-8abb-c4023f1b3aa9
Diffstat (limited to 'src/network_write_mmap.c')
-rw-r--r--src/network_write_mmap.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/src/network_write_mmap.c b/src/network_write_mmap.c
new file mode 100644
index 00000000..9747d619
--- /dev/null
+++ b/src/network_write_mmap.c
@@ -0,0 +1,157 @@
+#include "network_backends.h"
+
+#if defined(USE_MMAP)
+
+#include "network.h"
+#include "log.h"
+#include "sys-mmap.h"
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+
+#if 0
+/* read mmap()ed data into local buffer */
+#define LOCAL_BUFFERING 1
+#endif
+
+int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
+ chunk* const c = cq->first;
+ off_t offset, toSend, file_end;
+ ssize_t r;
+ size_t mmap_offset, mmap_avail;
+ const char *data;
+
+ force_assert(NULL != c);
+ force_assert(FILE_CHUNK == c->type);
+ force_assert(c->offset >= 0 && c->offset <= c->file.length);
+
+ offset = c->file.start + c->offset;
+ toSend = c->file.length - c->offset;
+ if (toSend > *p_max_bytes) toSend = *p_max_bytes;
+ file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
+
+ if (0 == toSend) {
+ chunkqueue_remove_finished_chunks(cq);
+ return 0;
+ }
+
+ if (0 != network_open_file_chunk(srv, con, cq)) return -1;
+
+ /* mmap the buffer if offset is outside old mmap area or not mapped at all */
+ if (MAP_FAILED == c->file.mmap.start
+ || offset < c->file.mmap.offset
+ || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
+
+ if (MAP_FAILED != c->file.mmap.start) {
+ munmap(c->file.mmap.start, c->file.mmap.length);
+ c->file.mmap.start = MAP_FAILED;
+ }
+
+ /* Optimizations for the future:
+ *
+ * adaptive mem-mapping
+ * the problem:
+ * we mmap() the whole file. If someone has alot large files and 32bit
+ * machine the virtual address area will be unrun and we will have a failing
+ * mmap() call.
+ * solution:
+ * only mmap 16M in one chunk and move the window as soon as we have finished
+ * the first 8M
+ *
+ * read-ahead buffering
+ * the problem:
+ * sending out several large files in parallel trashes the read-ahead of the
+ * kernel leading to long wait-for-seek times.
+ * solutions: (increasing complexity)
+ * 1. use madvise
+ * 2. use a internal read-ahead buffer in the chunk-structure
+ * 3. use non-blocking IO for file-transfers
+ * */
+
+ c->file.mmap.offset = offset & ~(4095); /* align at 4kb */
+
+ /* all mmap()ed areas are 512kb except the last which might be smaller */
+ c->file.mmap.length = 512*1024;
+ if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
+ c->file.mmap.length = file_end - c->file.mmap.offset;
+ }
+
+ if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) {
+ log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
+ strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
+ return -1;
+ }
+
+#if defined(LOCAL_BUFFERING)
+ buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length);
+#else
+# if defined(HAVE_MADVISE)
+ /* don't advise files < 64Kb */
+ if (c->file.mmap.length > (64*1024)) {
+ /* darwin 7 is returning EINVAL all the time and I don't know how to
+ * detect this at runtime.
+ *
+ * ignore the return value for now */
+ madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
+ }
+# endif
+#endif
+ }
+
+ force_assert(offset >= c->file.mmap.offset);
+ mmap_offset = offset - c->file.mmap.offset;
+ force_assert(c->file.mmap.length > mmap_offset);
+ mmap_avail = c->file.mmap.length - mmap_offset;
+ if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
+
+#if defined(LOCAL_BUFFERING)
+ data = c->mem->ptr + mmap_offset;
+#else
+ data = c->file.mmap.start + mmap_offset;
+#endif
+
+#if defined(__WIN32)
+ if ((r = send(fd, data, toSend, 0)) < 0) {
+ int lastError = WSAGetLastError();
+ switch (lastError) {
+ case WSAEINTR:
+ case WSAEWOULDBLOCK:
+ break;
+ case WSAECONNRESET:
+ case WSAETIMEDOUT:
+ case WSAECONNABORTED:
+ return -2;
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "sdd",
+ "send failed: ", lastError, fd);
+ return -1;
+ }
+ }
+#else /* __WIN32 */
+ if ((r = write(fd, data, toSend)) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ return -2;
+ default:
+ log_error_write(srv, __FILE__, __LINE__, "ssd",
+ "write failed:", strerror(errno), fd);
+ return -1;
+ }
+ }
+#endif /* __WIN32 */
+
+ if (r >= 0) {
+ *p_max_bytes -= r;
+ chunkqueue_mark_written(cq, r);
+ }
+
+ return (r > 0 && r == toSend) ? 0 : -3;
+}
+
+#endif /* USE_MMAP */