about summary refs log tree commit homepage
path: root/trywrite.c
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2012-01-11 21:46:04 +0000
committerEric Wong <normalperson@yhbt.net>2012-01-11 21:46:04 +0000
commit301b41b6f1350806a750794d615e3468735757a6 (patch)
tree54deb2b4cb0060a54746e3635746d0f338c294f5 /trywrite.c
downloadcmogstored-301b41b6f1350806a750794d615e3468735757a6.tar.gz
Nuked old history since it was missing copyright/GPLv3 notices.
Diffstat (limited to 'trywrite.c')
-rw-r--r--trywrite.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/trywrite.c b/trywrite.c
new file mode 100644
index 0000000..2e0fca7
--- /dev/null
+++ b/trywrite.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012, Eric Wong <normalperson@yhbt.net>
+ * License: GPLv3 or later (see COPYING for details)
+ */
+#include "cmogstored.h"
+
+struct mog_wbuf {
+        size_t len;
+        size_t off;
+        unsigned char buf[FLEXIBLE_ARRAY_MEMBER];
+};
+
+static void * wbuf_new(size_t total, struct iovec *iov, int iovcnt)
+{
+        struct mog_wbuf *wbuf = mog_cachealign(sizeof(struct mog_wbuf) + total);
+        void *dst = wbuf->buf;
+        int i;
+
+        wbuf->len = total;
+        wbuf->off = 0;
+
+        for (i = 0; i < iovcnt; i++)
+                dst = mempcpy(dst, iov[i].iov_base, iov[i].iov_len);
+
+        return wbuf;
+}
+
+enum mog_write_state mog_tryflush(int fd, struct mog_wbuf **x)
+{
+        struct mog_wbuf *wbuf = *x;
+        unsigned char *ptr = wbuf->buf + wbuf->off;
+        size_t len = wbuf->len - wbuf->off;
+
+        for (;;) {
+                ssize_t w = write(fd, ptr, len);
+
+                if (w == len) {
+                        mog_free_and_null(x);
+                        return MOG_WRSTATE_DONE;
+                }
+                if (w >= 0) {
+                        wbuf->off += w;
+                        ptr += w;
+                        len -= w;
+
+                        continue;
+                }
+
+                assert(w < 0 && "no error from write(2)");
+
+                switch (errno) {
+                case_EAGAIN: return MOG_WRSTATE_BUSY;
+                case EINTR: continue;
+                }
+
+                mog_free_and_null(x);
+                return MOG_WRSTATE_ERR;
+        }
+}
+
+/*
+ * returns
+ * - NULL on full write
+ * - MOG_WR_ERROR on error (and sets errno)
+ * - address to a new mog_wbuf with unbuffered contents on partial write
+ */
+void * mog_trywritev(int fd, struct iovec *iov, int iovcnt)
+{
+        ssize_t total = 0;
+        ssize_t w;
+        int i;
+
+        for (i = 0; i < iovcnt; i++)
+                total += iov[i].iov_len;
+
+        if (total == 0)
+                return NULL;
+retry:
+        w = writev(fd, iov, iovcnt);
+
+        if (w == total) {
+                return NULL;
+        } else if (w < 0) {
+                switch (errno) {
+                case_EAGAIN: return wbuf_new(total, iov, iovcnt);
+                case EINTR: goto retry;
+                }
+                return MOG_WR_ERROR;
+        } else {
+                struct iovec *new_iov = iov;
+
+                total -= w;
+
+                 /* skip over iovecs we've already written completely */
+                for (i = 0; i < iovcnt; i++, new_iov++) {
+                        if (w == 0)
+                                break;
+                        /*
+                         * partially written iovec,
+                         * modify and retry with current iovec in front
+                         */
+                        if (new_iov->iov_len > (size_t)w) {
+                                unsigned char *base = new_iov->iov_base;
+
+                                new_iov->iov_len -= w;
+                                base += w;
+                                new_iov->iov_base = (void *)base;
+                                break;
+                        }
+
+                        w -= new_iov->iov_len;
+                }
+
+                /* retry without the already-written iovecs */
+                iovcnt -= i;
+                iov = new_iov;
+                goto retry;
+        }
+}