diff --git a/include/iobuf.h b/include/iobuf.h index deaf0ffaf21d4d598db7e1a28fa17a88ab23941c..60ea3fb88ddb8cd1bce304c6c8115e829ae58b48 100644 --- a/include/iobuf.h +++ b/include/iobuf.h @@ -136,39 +136,39 @@ obuf_iovcnt(struct obuf *buf) return buf->iov[buf->pos].iov_len > 0 ? buf->pos + 1 : buf->pos; } +/** + * Output buffer savepoint. It's possible to + * save the current buffer state in a savepoint + * and roll back to the saved state at any time + * before iobuf_flush() + */ +struct obuf_svp +{ + size_t pos; + size_t iov_len; + size_t size; +}; + /** * Reserve size bytes in the output buffer * and return a pointer to the reserved * data. Returns a pointer to a continuous piece of * memory. * Typical use case: - * void *psize = obuf_book(buf, sizeof(uint32_t)); + * struct obuf_svp svp = obuf_book(buf, sizeof(uint32_t)); * for (...) * obuf_dup(buf, ...); * uint32_t total = obuf_size(buf); - * memcpy(psize, &total, sizeof(total); + * memcpy(obuf_svp_to_ptr(&svp), &total, sizeof(total); * iobuf_flush(); */ -void * +struct obuf_svp obuf_book(struct obuf *obuf, size_t size); /** Append data to the output buffer. */ void obuf_dup(struct obuf *obuf, void *data, size_t size); -/** - * Output buffer savepoint. It's possible to - * save the current buffer state in a savepoint - * and roll back to the saved state at any time - * before iobuf_flush() - */ -struct obuf_svp -{ - size_t pos; - size_t iov_len; - size_t size; -}; - static inline struct obuf_svp obuf_create_svp(struct obuf *buf) { diff --git a/src/iobuf.m b/src/iobuf.m index d230a0f735587e1872bd3d9d0cd89823335cc83d..a54b722d7c1163ec8bc4c468da9b0ae8ca892eba 100644 --- a/src/iobuf.m +++ b/src/iobuf.m @@ -200,7 +200,7 @@ obuf_dup(struct obuf *buf, void *data, size_t size) } /** Book a few bytes in the output buffer. */ -void * +struct obuf_svp obuf_book(struct obuf *buf, size_t size) { struct iovec *iov = &buf->iov[buf->pos]; @@ -221,11 +221,13 @@ obuf_book(struct obuf *buf, size_t size) obuf_alloc_pos(buf, buf->pos, size); } } - void *booking = iov->iov_base + iov->iov_len; + struct obuf_svp svp = { + .pos = buf->pos, .iov_len = iov->iov_len, .size = buf->size + }; iov->iov_len += size; buf->size += size; assert(iov->iov_len <= buf->capacity[buf->pos]); - return booking; + return svp; } /** Forget about data in the output buffer beyond the savepoint. */ diff --git a/src/iproto.m b/src/iproto.m index d3fa59411c86380a65a71d3e38f4687a39447c85..4e859b29cbad7d1ea8a9c8af45293790659f2497 100644 --- a/src/iproto.m +++ b/src/iproto.m @@ -138,8 +138,7 @@ port_iproto_add_tuple(struct port *ptr, struct tuple *tuple, u32 flags) struct port_iproto *port = port_iproto(ptr); if (++port->reply.found == 1) { /* Found the first tuple, add header. */ - port->svp = obuf_create_svp(port->buf); - obuf_book(port->buf, sizeof(port->reply)); + port->svp = obuf_book(port->buf, sizeof(port->reply)); } if (flags & BOX_RETURN_TUPLE) { obuf_dup(port->buf, &tuple->bsize, tuple_len(tuple)); @@ -635,7 +634,14 @@ iproto_session_output_iobuf(struct iproto_session *session) { if (obuf_size(&session->iobuf[1]->out)) return session->iobuf[1]; - if (obuf_size(&session->iobuf[0]->out)) + /* + * Don't try to write from a newer buffer if an older one + * exists: in case of a partial write of a newer buffer, + * the client may end up getting a salad of different + * pieces of replies from both buffers. + */ + if (ibuf_size(&session->iobuf[1]->in) == 0 && + obuf_size(&session->iobuf[0]->out)) return session->iobuf[0]; return NULL; }