diff options
author | Eric Wong <normalperson@yhbt.net> | 2012-01-11 21:46:04 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2012-01-11 21:46:04 +0000 |
commit | 301b41b6f1350806a750794d615e3468735757a6 (patch) | |
tree | 54deb2b4cb0060a54746e3635746d0f338c294f5 /trywrite.c | |
download | cmogstored-301b41b6f1350806a750794d615e3468735757a6.tar.gz |
Nuked old history since it was missing copyright/GPLv3 notices.
Diffstat (limited to 'trywrite.c')
-rw-r--r-- | trywrite.c | 119 |
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; + } +} |