cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / trywrite.c
blobaf755137a63a6a65be7362d182557b206f2bec27
1 /*
2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4 */
5 #include "cmogstored.h"
7 struct mog_wbuf {
8 size_t len;
9 size_t off;
10 unsigned char buf[FLEXIBLE_ARRAY_MEMBER];
13 static void * wbuf_newv(size_t total, struct iovec *iov, int iovcnt)
15 struct mog_wbuf *wbuf = malloc(sizeof(struct mog_wbuf) + total);
16 void *dst;
17 int i;
19 if (!wbuf) return MOG_WR_ERROR;
21 dst = wbuf->buf;
23 wbuf->len = total;
24 wbuf->off = 0;
26 for (i = 0; i < iovcnt; i++)
27 dst = mempcpy(dst, iov[i].iov_base, iov[i].iov_len);
29 return wbuf;
32 static void * wbuf_new(void *buf, size_t len)
34 struct iovec iov;
36 iov.iov_base = buf;
37 iov.iov_len = len;
39 return wbuf_newv(len, &iov, 1);
42 MOG_NOINLINE static void sysbug(const char *fn, ssize_t bytes)
44 syslog(LOG_ERR, "%s returned %zd bytes written but: %m", fn, bytes);
47 enum mog_write_state mog_tryflush(int fd, struct mog_wbuf **x)
49 struct mog_wbuf *wbuf = *x;
50 unsigned char *ptr = wbuf->buf + wbuf->off;
51 size_t len = wbuf->len - wbuf->off;
53 for (;;) {
54 ssize_t w = write(fd, ptr, len);
56 if (w == len) {
57 mog_free_and_null(x);
58 return MOG_WRSTATE_DONE;
60 if (w >= 0) {
61 wbuf->off += w;
62 ptr += w;
63 len -= w;
65 continue;
68 assert(w < 0 && "no error from write(2)");
70 switch (errno) {
71 case_EAGAIN: return MOG_WRSTATE_BUSY;
72 case EINTR: continue;
75 mog_free_and_null(x);
76 return MOG_WRSTATE_ERR;
81 * returns
82 * - NULL on full write
83 * - MOG_WR_ERROR on error (and sets errno)
84 * - address to a new mog_wbuf with unbuffered contents on partial write
86 void * mog_trywritev(int fd, struct iovec *iov, int iovcnt)
88 ssize_t len = 0;
89 ssize_t w;
90 int i;
92 for (i = 0; i < iovcnt; i++)
93 len += iov[i].iov_len;
95 if (len == 0)
96 return NULL;
97 retry:
98 w = writev(fd, iov, iovcnt);
100 if (w == len) {
101 return NULL;
102 } else if (w <= 0) {
103 switch (errno) {
104 case_EAGAIN:
105 TRACE(CMOGSTORED_WRITE_BUFFERED(fd, len));
106 return wbuf_newv(len, iov, iovcnt);
107 case EINTR: goto retry;
108 case 0: sysbug("writev", w);
110 return MOG_WR_ERROR;
111 } else {
112 struct iovec *new_iov = iov;
114 len -= w;
116 /* skip over iovecs we've already written completely */
117 for (i = 0; i < iovcnt; i++, new_iov++) {
118 if (w == 0)
119 break;
121 * partially written iovec,
122 * modify and retry with current iovec in front
124 if (new_iov->iov_len > (size_t)w) {
125 unsigned char *base = new_iov->iov_base;
127 new_iov->iov_len -= w;
128 base += w;
129 new_iov->iov_base = (void *)base;
130 break;
133 w -= new_iov->iov_len;
136 /* retry without the already-written iovecs */
137 iovcnt -= i;
138 iov = new_iov;
139 goto retry;
144 * returns
145 * - NULL on full write
146 * - MOG_WR_ERROR on error (and sets errno)
147 * - address to a new mog_wbuf with unbuffered contents on partial write
149 void * mog_trysend(int fd, void *buf, size_t len, off_t more)
151 if (MOG_MSG_MORE) {
152 int flags = more > 0 ? MOG_MSG_MORE : 0;
154 while (len > 0) {
155 ssize_t w = send(fd, buf, len, flags);
157 if (w == (ssize_t)len)
158 return NULL; /* all done */
159 if (w > 0) {
160 buf = (char *)buf + w;
161 len -= w;
162 continue;
166 * we bail on w == 0, too. send should normally
167 * return zero, but in case there's a kernel bug
168 * we should not infinite loop
170 switch (errno) {
171 case_EAGAIN:
172 TRACE(CMOGSTORED_WRITE_BUFFERED(fd, len));
173 return wbuf_new(buf, len);
174 case EINTR: continue;
175 case 0: sysbug("send", w);
177 return MOG_WR_ERROR;
180 return NULL;
181 } else {
182 struct iovec iov;
184 iov.iov_base = buf;
185 iov.iov_len = len;
187 return mog_trywritev(fd, &iov, 1);