From 62efbe3e7fff6a85aeb5fbbe8c28495f37276743 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Fri, 12 Oct 2012 23:36:09 +0400 Subject: [PATCH] Introduce struct iobuf. Extract buffered fiber I/O. Change memcached, admin and iproto connection to use the buffered IO. Make coio_writev accept a hint about the total size. Remove fiber iov. --- include/coio.h | 2 +- include/coio_buf.h | 30 +- include/fiber.h | 55 +- include/fio.h | 2 +- include/iobuf.h | 213 ++++++++ include/iproto.h | 13 +- mod/box/box.m | 10 +- mod/box/memcached-grammar.m | 977 ++++++++++++++++++----------------- mod/box/memcached-grammar.rl | 93 ++-- mod/box/memcached.m | 114 ++-- mod/box/port.h | 11 +- mod/box/port.m | 48 +- src/CMakeLists.txt | 1 + src/admin.m | 122 ++--- src/admin.rl | 18 +- src/coio.m | 22 +- src/coio_buf.m | 1 - src/fiber.m | 104 +--- src/iobuf.m | 339 ++++++++++++ src/iproto.m | 82 +-- src/palloc.m | 8 +- src/replica.m | 36 +- src/tarantool_lua.m | 1 + test/box/admin.result | 1 + test/box/admin.test | 1 + test/lib/admin_connection.py | 2 +- 26 files changed, 1383 insertions(+), 923 deletions(-) create mode 100644 include/iobuf.h create mode 100644 src/iobuf.m diff --git a/include/coio.h b/include/coio.h index 5907e8c070..6bbd26d112 100644 --- a/include/coio.h +++ b/include/coio.h @@ -87,7 +87,7 @@ void coio_write(struct coio *coio, const void *buf, size_t sz); ssize_t -coio_writev(struct coio *coio, struct iovec *iov, int iovcnt); +coio_writev(struct coio *coio, struct iovec *iov, int iovcnt, size_t size); void coio_service_init(struct coio_service *service, const char *name, diff --git a/include/coio_buf.h b/include/coio_buf.h index 8c8e8ffb5d..94d23bc28e 100644 --- a/include/coio_buf.h +++ b/include/coio_buf.h @@ -29,34 +29,26 @@ * SUCH DAMAGE. */ #include "coio.h" -#include "tbuf.h" +#include "iobuf.h" /** Buffered cooperative IO */ -extern int coio_readahead; - -static inline void -coio_binit(int readahead) -{ - coio_readahead = readahead; -} - +/** Read at least sz bytes, buffered. Return 0 in case of EOF. */ static inline ssize_t -coio_bread(struct coio *coio, struct tbuf *buf, size_t sz) +coio_bread(struct coio *coio, struct ibuf *buf, size_t sz) { - tbuf_ensure(buf, MAX(sz, coio_readahead)); - ssize_t n = coio_read_ahead(coio, tbuf_end(buf), - sz, tbuf_unused(buf)); - buf->size += n; + ibuf_reserve(buf, sz); + ssize_t n = coio_read_ahead(coio, buf->end, sz, ibuf_unused(buf)); + buf->end += n; return n; } +/** Read at least sz bytes, buffered. Throw an exception in case of EOF. */ static inline ssize_t -coio_breadn(struct coio *coio, struct tbuf *buf, size_t sz) +coio_breadn(struct coio *coio, struct ibuf *buf, size_t sz) { - tbuf_ensure(buf, MAX(sz, coio_readahead)); - ssize_t n = coio_readn_ahead(coio, tbuf_end(buf), - sz, tbuf_unused(buf)); - buf->size += n; + ibuf_reserve(buf, sz); + ssize_t n = coio_readn_ahead(coio, buf->end, sz, ibuf_unused(buf)); + buf->end += n; return n; } diff --git a/include/fiber.h b/include/fiber.h index 2c196f2253..d5ddda671c 100644 --- a/include/fiber.h +++ b/include/fiber.h @@ -33,11 +33,7 @@ #include <stdbool.h> #include <stdint.h> #include <unistd.h> -#include <sys/uio.h> -#include <netinet/in.h> - #include <tarantool_ev.h> -#include <tbuf.h> #include <coro.h> #include <util.h> #include "third_party/queue.h" @@ -76,11 +72,6 @@ struct fiber { ev_timer timer; ev_child cw; - struct tbuf iov; - size_t iov_cnt; - struct tbuf rbuf; - struct tbuf cleanup; - SLIST_ENTRY(fiber) link, zombie_link; /* ASCIIZ name of this fiber. */ @@ -91,14 +82,6 @@ struct fiber { struct fiber *waiter; }; -static inline struct iovec *iovec(const struct tbuf *t) -{ - return (struct iovec *)t->data; -} - -typedef void (*fiber_cleanup_handler) (void *); -void fiber_register_cleanup(fiber_cleanup_handler handler, void *data); - extern __thread struct fiber *fiber; void fiber_init(void); @@ -115,43 +98,6 @@ fiber_yield_to(struct fiber *f); void fiber_destroy_all(); -inline static void iov_add_unsafe(const void *buf, size_t len) -{ - struct iovec *v; - assert(tbuf_unused(&fiber->iov) >= sizeof(*v)); - v = tbuf_end(&fiber->iov); - v->iov_base = (void *)buf; - v->iov_len = len; - fiber->iov.size += sizeof(*v); - fiber->iov_cnt++; -} - -inline static void iov_ensure(size_t count) -{ - tbuf_ensure(&fiber->iov, sizeof(struct iovec) * count); -} - -/* Add to fiber's iov vector. */ -inline static void iov_add(const void *buf, size_t len) -{ - iov_ensure(1); - iov_add_unsafe(buf, len); -} - -inline static void iov_dup(const void *buf, size_t len) -{ - void *copy = palloc(fiber->gc_pool, len); - memcpy(copy, buf, len); - iov_add(copy, len); -} - -struct coio; -/* Reset the fiber's iov vector. */ -ssize_t iov_flush(struct coio *coio); -/* Write everything in the fiber's iov vector to fiber socket. */ -void iov_reset(); - -void fiber_cleanup(void); void fiber_gc(void); void fiber_call(struct fiber *callee, ...); void fiber_wakeup(struct fiber *f); @@ -172,6 +118,7 @@ void fiber_testcancel(void); */ void fiber_setcancelstate(bool enable); void fiber_sleep(ev_tstamp s); +struct tbuf; void fiber_info(struct tbuf *out); void fiber_schedule(ev_watcher *watcher, int event __attribute__((unused))); diff --git a/include/fio.h b/include/fio.h index e6a30fafed..f2c2a072d6 100644 --- a/include/fio.h +++ b/include/fio.h @@ -149,7 +149,7 @@ struct fio_batch ssize_t bytes; /** Total number of batched rows.*/ int rows; - /** A cap on how many can be batched. Can be set to INT_MAX. */ + /** A cap on how many rows can be batched. Can be set to INT_MAX. */ int max_rows; /** A system cap on how many rows can be batched. */ long max_iov; diff --git a/include/iobuf.h b/include/iobuf.h new file mode 100644 index 0000000000..34bdad90db --- /dev/null +++ b/include/iobuf.h @@ -0,0 +1,213 @@ +#ifndef TARANTOOL_IOBUF_H_INCLUDED +#define TARANTOOL_IOBUF_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/uio.h> +#include "util.h" +#include "third_party/queue.h" + +/** {{{ Input buffer. + * + * Continuous piece of memory to store input. + * Allocated in factors of cfg.readahead. + * Maintains position of the data "to be processed". + * + * Typical use case: + * + * struct ibuf *in; + * coio_bread(coio, in, request_len); + * if (ibuf_size(in) >= request_len) { + * process_request(in->pos, request_len); + * in->pos += request_len; + * } + */ +struct ibuf +{ + struct palloc_pool *pool; + char *buf; + /** Start of input. */ + char *pos; + /** End of useful input */ + char *end; + /** Buffer size. */ + size_t capacity; +}; + +/** Reserve space for sz bytes in the input buffer. */ +void +ibuf_reserve(struct ibuf *ibuf, size_t sz); + +/** How much data is read and is not parsed yet. */ +static inline size_t +ibuf_size(struct ibuf *ibuf) +{ + return ibuf->end - ibuf->pos; +} + +/** How much data can we fit beyond buf->end */ +static inline size_t +ibuf_unused(struct ibuf *ibuf) +{ + return ibuf->buf + ibuf->capacity - ibuf->end; +} + +/* }}} */ + +/* {{{ Output buffer. */ + +enum { IOBUF_IOV_MAX = 32 }; + +/** + * An output buffer is an array of struct iovec vectors + * for writev(). + * Each buffer is allocated on palloc pool. + * Buffer size grows by a factor of 2. With this growth factor, + * the number of used buffers is unlikely to ever exceed the + * hard limit of IOBUF_IOV_MAX. If it does, an exception is + * raised. + */ +struct obuf +{ + struct palloc_pool *pool; + /* How many bytes are in the buffer. */ + size_t size; + /** Position of the "current" iovec. */ + size_t pos; + /** How many bytes are actually allocated for each iovec. */ + size_t capacity[IOBUF_IOV_MAX]; + /** + * List of iovec vectors, each vector is at least twice + * as big as the previous one. The vector following the + * last allocated one is always zero-initialized + * (iov_base = NULL, iov_len = 0). + */ + struct iovec iov[IOBUF_IOV_MAX]; +}; + +/** How many bytes are in the output buffer. */ +static inline size_t +obuf_size(struct obuf *obuf) +{ + return obuf->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)); + * for (...) + * obuf_dup(buf, ...); + * uint32_t total = obuf_size(buf); + * memcpy(psize, &total, sizeof(total); + * iobuf_flush(); + */ +void * +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) +{ + struct obuf_svp svp = { + .pos = buf->pos, .iov_len = buf->iov[buf->pos].iov_len, + .size = buf->size + }; + return svp; +} + +/** Forget anything added to output buffer after the savepoint. */ +void +obuf_rollback_to_svp(struct obuf *buf, struct obuf_svp *svp); + +/* }}} */ + +/** {{{ Input/output pair. */ +struct iobuf +{ + /** Used for iobuf cache. */ + SLIST_ENTRY(iobuf) next; + /** Input buffer. */ + struct ibuf in; + /** Output buffer. */ + struct obuf out; +}; + +/** Create an instance of input/output buffer. */ +struct iobuf * +iobuf_create(const char *name); + +/** Destroy an input/output buffer. */ +void +iobuf_destroy(struct iobuf *iobuf); + +struct coio; + +/** Flush output using cooperative I/O and garbage collect. + * @return number of bytes written + */ +ssize_t +iobuf_flush(struct iobuf *iobuf, struct coio *coio); + +/** + * Must be called when we are done sending all output, + * and there is likely no cached input. + * Is automatically called by iobuf_flush(). + */ +void +iobuf_gc(struct iobuf *iobuf); + +extern int cfg_readahead; + +static inline void +iobuf_init_readahead(int readahead) +{ + cfg_readahead = readahead; +} + +/* }}} */ + +#endif /* TARANTOOL_IOBUF_H_INCLUDED */ diff --git a/include/iproto.h b/include/iproto.h index e36b463bef..2168d11544 100644 --- a/include/iproto.h +++ b/include/iproto.h @@ -29,8 +29,10 @@ * SUCH DAMAGE. */ #include <stdint.h> +#include <stdarg.h> -#include <tbuf.h> /* for struct tbuf */ +struct tbuf; +struct obuf; enum { /** Maximal iproto package body length (2GiB) */ @@ -56,13 +58,8 @@ struct iproto_header_retcode { uint32_t ret_code; } __attribute__((packed)); -static inline struct iproto_header *iproto(const struct tbuf *t) -{ - return (struct iproto_header *)t->data; -} - - -typedef void (*iproto_callback) (uint32_t msg_code, struct tbuf *request); +typedef void (*iproto_callback)(struct obuf *obuf, + uint32_t msg_code, struct tbuf *request); void iproto_interact(va_list ap); diff --git a/mod/box/box.m b/mod/box/box.m index 6f54431f05..5dd51e7cd0 100644 --- a/mod/box/box.m +++ b/mod/box/box.m @@ -127,15 +127,17 @@ box_process_ro(struct txn *txn, struct port *port, static void -iproto_primary_port_handler(u32 op, struct tbuf *request_data) +iproto_primary_port_handler(struct obuf *out, u32 op, + struct tbuf *request_data) { - box_process(txn_begin(), port_iproto_create(), op, request_data); + box_process(txn_begin(), port_iproto_create(out), op, request_data); } static void -iproto_secondary_port_handler(u32 op, struct tbuf *request_data) +iproto_secondary_port_handler(struct obuf *out, u32 op, + struct tbuf *request_data) { - box_process_ro(txn_begin(), port_iproto_create(), op, request_data); + box_process_ro(txn_begin(), port_iproto_create(out), op, request_data); } static void diff --git a/mod/box/memcached-grammar.m b/mod/box/memcached-grammar.m index 67f9295a4f..90594c4766 100644 --- a/mod/box/memcached-grammar.m +++ b/mod/box/memcached-grammar.m @@ -42,11 +42,11 @@ static const int memcached_en_main = 1; static int __attribute__((noinline)) -memcached_dispatch(struct coio *coio) +memcached_dispatch(struct coio *coio, struct iobuf *iobuf) { int cs; - u8 *p, *pe; - u8 *fstart; + char *p, *pe; + char *fstart; struct tbuf *keys = tbuf_alloc(fiber->gc_pool); void *key; bool append, show_cas; @@ -54,24 +54,27 @@ memcached_dispatch(struct coio *coio) u64 cas, incr; u32 flags, exptime, bytes; bool noreply = false; - u8 *data = NULL; + char *data = NULL; bool done = false; - size_t saved_iov_cnt = fiber->iov_cnt; uintptr_t flush_delay = 0; size_t keys_count = 0; + struct ibuf *in = &iobuf->in; + struct obuf *out = &iobuf->out; + /* Savepoint for 'noreply' */ + struct obuf_svp obuf_svp = obuf_create_svp(out); - p = fiber->rbuf.data; - pe = fiber->rbuf.data + fiber->rbuf.size; + p = in->pos; + pe = in->end; say_debug("memcached_dispatch '%.*s'", MIN((int)(pe - p), 40) , p); -#line 70 "mod/box/memcached-grammar.m" +#line 73 "mod/box/memcached-grammar.m" { cs = memcached_start; } -#line 75 "mod/box/memcached-grammar.m" +#line 78 "mod/box/memcached-grammar.m" { if ( p == pe ) goto _test_eof; @@ -143,7 +146,7 @@ case 5: goto st0; goto tr15; tr15: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -160,7 +163,7 @@ st6: if ( ++p == pe ) goto _test_eof6; case 6: -#line 164 "mod/box/memcached-grammar.m" +#line 167 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st7; goto st0; @@ -174,49 +177,49 @@ case 7: goto tr17; goto st0; tr17: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st8; st8: if ( ++p == pe ) goto _test_eof8; case 8: -#line 185 "mod/box/memcached-grammar.m" +#line 188 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr18; if ( 48 <= (*p) && (*p) <= 57 ) goto st8; goto st0; tr18: -#line 243 "mod/box/memcached-grammar.rl" +#line 246 "mod/box/memcached-grammar.rl" {flags = natoq(fstart, p);} goto st9; st9: if ( ++p == pe ) goto _test_eof9; case 9: -#line 199 "mod/box/memcached-grammar.m" +#line 202 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st9; if ( 48 <= (*p) && (*p) <= 57 ) goto tr21; goto st0; tr21: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st10; st10: if ( ++p == pe ) goto _test_eof10; case 10: -#line 213 "mod/box/memcached-grammar.m" +#line 216 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr22; if ( 48 <= (*p) && (*p) <= 57 ) goto st10; goto st0; tr22: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -227,21 +230,21 @@ st11: if ( ++p == pe ) goto _test_eof11; case 11: -#line 231 "mod/box/memcached-grammar.m" +#line 234 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st11; if ( 48 <= (*p) && (*p) <= 57 ) goto tr25; goto st0; tr25: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st12; st12: if ( ++p == pe ) goto _test_eof12; case 12: -#line 245 "mod/box/memcached-grammar.m" +#line 248 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr26; case 13: goto tr27; @@ -251,20 +254,23 @@ case 12: goto st12; goto st0; tr26: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -274,35 +280,38 @@ tr26: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 65 "mod/box/memcached-grammar.rl" +#line 68 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple != NULL && !expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr30: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -312,37 +321,40 @@ tr30: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 65 "mod/box/memcached-grammar.rl" +#line 68 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple != NULL && !expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr39: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -352,37 +364,40 @@ tr39: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 65 "mod/box/memcached-grammar.rl" +#line 68 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple != NULL && !expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr58: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -392,13 +407,13 @@ tr58: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 94 "mod/box/memcached-grammar.rl" +#line 97 "mod/box/memcached-grammar.rl" { struct tbuf *b; void *value; @@ -407,7 +422,7 @@ tr58: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST) { - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); } else { value = tuple_field(tuple, 3); value_len = load_varint32(&value); @@ -427,18 +442,21 @@ tr58: } goto st197; tr62: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -448,13 +466,13 @@ tr62: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 94 "mod/box/memcached-grammar.rl" +#line 97 "mod/box/memcached-grammar.rl" { struct tbuf *b; void *value; @@ -463,7 +481,7 @@ tr62: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST) { - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); } else { value = tuple_field(tuple, 3); value_len = load_varint32(&value); @@ -483,20 +501,23 @@ tr62: } goto st197; tr71: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -506,13 +527,13 @@ tr71: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 94 "mod/box/memcached-grammar.rl" +#line 97 "mod/box/memcached-grammar.rl" { struct tbuf *b; void *value; @@ -521,7 +542,7 @@ tr71: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST) { - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); } else { value = tuple_field(tuple, 3); value_len = load_varint32(&value); @@ -541,20 +562,23 @@ tr71: } goto st197; tr91: -#line 245 "mod/box/memcached-grammar.rl" +#line 248 "mod/box/memcached-grammar.rl" {cas = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -564,37 +588,40 @@ tr91: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 83 "mod/box/memcached-grammar.rl" +#line 86 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); else if (meta(tuple)->cas != cas) - iov_add("EXISTS\r\n", 8); + obuf_dup(out, "EXISTS\r\n", 8); else STORE; } goto st197; tr95: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -604,39 +631,42 @@ tr95: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 83 "mod/box/memcached-grammar.rl" +#line 86 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); else if (meta(tuple)->cas != cas) - iov_add("EXISTS\r\n", 8); + obuf_dup(out, "EXISTS\r\n", 8); else STORE; } goto st197; tr105: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -646,36 +676,36 @@ tr105: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 83 "mod/box/memcached-grammar.rl" +#line 86 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); else if (meta(tuple)->cas != cas) - iov_add("EXISTS\r\n", 8); + obuf_dup(out, "EXISTS\r\n", 8); else STORE; } goto st197; tr118: -#line 246 "mod/box/memcached-grammar.rl" +#line 249 "mod/box/memcached-grammar.rl" {incr = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 121 "mod/box/memcached-grammar.rl" +#line 124 "mod/box/memcached-grammar.rl" { struct meta *m; struct tbuf *b; @@ -686,7 +716,7 @@ tr118: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { m = meta(tuple); field = tuple_field(tuple, 3); @@ -716,31 +746,31 @@ tr118: @try { store(key, exptime, flags, bytes, data); stats.total_items++; - iov_add(b->data, b->size); - iov_add("\r\n", 2); + obuf_dup(out, b->data, b->size); + obuf_dup(out, "\r\n", 2); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } else { - iov_add("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); + obuf_dup(out, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); } } } goto st197; tr122: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 121 "mod/box/memcached-grammar.rl" +#line 124 "mod/box/memcached-grammar.rl" { struct meta *m; struct tbuf *b; @@ -751,7 +781,7 @@ tr122: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { m = meta(tuple); field = tuple_field(tuple, 3); @@ -781,33 +811,33 @@ tr122: @try { store(key, exptime, flags, bytes, data); stats.total_items++; - iov_add(b->data, b->size); - iov_add("\r\n", 2); + obuf_dup(out, b->data, b->size); + obuf_dup(out, "\r\n", 2); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } else { - iov_add("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); + obuf_dup(out, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); } } } goto st197; tr132: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 121 "mod/box/memcached-grammar.rl" +#line 124 "mod/box/memcached-grammar.rl" { struct meta *m; struct tbuf *b; @@ -818,7 +848,7 @@ tr132: key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { m = meta(tuple); field = tuple_field(tuple, 3); @@ -848,215 +878,218 @@ tr132: @try { store(key, exptime, flags, bytes, data); stats.total_items++; - iov_add(b->data, b->size); - iov_add("\r\n", 2); + obuf_dup(out, b->data, b->size); + obuf_dup(out, "\r\n", 2); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } else { - iov_add("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); + obuf_dup(out, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); } } } goto st197; tr141: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 176 "mod/box/memcached-grammar.rl" +#line 179 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { @try { delete(key); - iov_add("DELETED\r\n", 9); + obuf_dup(out, "DELETED\r\n", 9); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } } goto st197; tr146: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) exptime = exptime + ev_now(); } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 176 "mod/box/memcached-grammar.rl" +#line 179 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { @try { delete(key); - iov_add("DELETED\r\n", 9); + obuf_dup(out, "DELETED\r\n", 9); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } } goto st197; tr157: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 176 "mod/box/memcached-grammar.rl" +#line 179 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { @try { delete(key); - iov_add("DELETED\r\n", 9); + obuf_dup(out, "DELETED\r\n", 9); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } } goto st197; tr169: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 205 "mod/box/memcached-grammar.rl" +#line 208 "mod/box/memcached-grammar.rl" { struct fiber *f = fiber_create("flush_all", flush_all); fiber_call(f, flush_delay); - iov_add("OK\r\n", 4); + obuf_dup(out, "OK\r\n", 4); } goto st197; tr174: -#line 247 "mod/box/memcached-grammar.rl" +#line 250 "mod/box/memcached-grammar.rl" {flush_delay = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 205 "mod/box/memcached-grammar.rl" +#line 208 "mod/box/memcached-grammar.rl" { struct fiber *f = fiber_create("flush_all", flush_all); fiber_call(f, flush_delay); - iov_add("OK\r\n", 4); + obuf_dup(out, "OK\r\n", 4); } goto st197; tr185: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 205 "mod/box/memcached-grammar.rl" +#line 208 "mod/box/memcached-grammar.rl" { struct fiber *f = fiber_create("flush_all", flush_all); fiber_call(f, flush_delay); - iov_add("OK\r\n", 4); + obuf_dup(out, "OK\r\n", 4); } goto st197; tr195: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 194 "mod/box/memcached-grammar.rl" +#line 197 "mod/box/memcached-grammar.rl" { @try { - memcached_get(keys_count, keys, show_cas); + memcached_get(out, keys_count, keys, show_cas); } @catch (ClientError *e) { - iov_reset(); - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_rollback_to_svp(out, &obuf_svp); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } goto st197; tr213: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 215 "mod/box/memcached-grammar.rl" +#line 218 "mod/box/memcached-grammar.rl" { return -1; } goto st197; tr233: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1066,35 +1099,38 @@ tr233: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 74 "mod/box/memcached-grammar.rl" +#line 77 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr237: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1104,37 +1140,40 @@ tr237: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 74 "mod/box/memcached-grammar.rl" +#line 77 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr246: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1144,37 +1183,40 @@ tr246: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 74 "mod/box/memcached-grammar.rl" +#line 77 "mod/box/memcached-grammar.rl" { key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } goto st197; tr263: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1184,31 +1226,34 @@ tr263: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 60 "mod/box/memcached-grammar.rl" +#line 63 "mod/box/memcached-grammar.rl" { key = read_field(keys); STORE; } goto st197; tr267: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1218,33 +1263,36 @@ tr267: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 60 "mod/box/memcached-grammar.rl" +#line 63 "mod/box/memcached-grammar.rl" { key = read_field(keys); STORE; } goto st197; tr276: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 249 "mod/box/memcached-grammar.rl" +#line 252 "mod/box/memcached-grammar.rl" { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -1254,63 +1302,63 @@ tr276: goto exit; } } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 60 "mod/box/memcached-grammar.rl" +#line 63 "mod/box/memcached-grammar.rl" { key = read_field(keys); STORE; } goto st197; tr281: -#line 274 "mod/box/memcached-grammar.rl" +#line 280 "mod/box/memcached-grammar.rl" { p++; } -#line 268 "mod/box/memcached-grammar.rl" +#line 274 "mod/box/memcached-grammar.rl" { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } -#line 211 "mod/box/memcached-grammar.rl" +#line 214 "mod/box/memcached-grammar.rl" { - print_stats(); + print_stats(out); } goto st197; st197: if ( ++p == pe ) goto _test_eof197; case 197: -#line 1288 "mod/box/memcached-grammar.m" +#line 1336 "mod/box/memcached-grammar.m" goto st0; tr27: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st13; tr40: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st13; st13: if ( ++p == pe ) goto _test_eof13; case 13: -#line 1302 "mod/box/memcached-grammar.m" +#line 1350 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr30; goto st0; tr28: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st14; st14: if ( ++p == pe ) goto _test_eof14; case 14: -#line 1314 "mod/box/memcached-grammar.m" +#line 1362 "mod/box/memcached-grammar.m" switch( (*p) ) { case 32: goto st14; case 78: goto st15; @@ -1424,18 +1472,18 @@ case 26: goto tr45; goto st0; tr45: -#line 282 "mod/box/memcached-grammar.rl" +#line 288 "mod/box/memcached-grammar.rl" {append = true; } goto st27; tr209: -#line 283 "mod/box/memcached-grammar.rl" +#line 289 "mod/box/memcached-grammar.rl" {append = false;} goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 1439 "mod/box/memcached-grammar.m" +#line 1487 "mod/box/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st27; @@ -1444,7 +1492,7 @@ case 27: goto st0; goto tr46; tr46: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1461,7 +1509,7 @@ st28: if ( ++p == pe ) goto _test_eof28; case 28: -#line 1465 "mod/box/memcached-grammar.m" +#line 1513 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st29; goto st0; @@ -1475,49 +1523,49 @@ case 29: goto tr49; goto st0; tr49: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st30; st30: if ( ++p == pe ) goto _test_eof30; case 30: -#line 1486 "mod/box/memcached-grammar.m" +#line 1534 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr50; if ( 48 <= (*p) && (*p) <= 57 ) goto st30; goto st0; tr50: -#line 243 "mod/box/memcached-grammar.rl" +#line 246 "mod/box/memcached-grammar.rl" {flags = natoq(fstart, p);} goto st31; st31: if ( ++p == pe ) goto _test_eof31; case 31: -#line 1500 "mod/box/memcached-grammar.m" +#line 1548 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st31; if ( 48 <= (*p) && (*p) <= 57 ) goto tr53; goto st0; tr53: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st32; st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 1514 "mod/box/memcached-grammar.m" +#line 1562 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr54; if ( 48 <= (*p) && (*p) <= 57 ) goto st32; goto st0; tr54: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -1528,21 +1576,21 @@ st33: if ( ++p == pe ) goto _test_eof33; case 33: -#line 1532 "mod/box/memcached-grammar.m" +#line 1580 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st33; if ( 48 <= (*p) && (*p) <= 57 ) goto tr57; goto st0; tr57: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st34; st34: if ( ++p == pe ) goto _test_eof34; case 34: -#line 1546 "mod/box/memcached-grammar.m" +#line 1594 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr58; case 13: goto tr59; @@ -1552,30 +1600,30 @@ case 34: goto st34; goto st0; tr59: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st35; tr72: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st35; st35: if ( ++p == pe ) goto _test_eof35; case 35: -#line 1567 "mod/box/memcached-grammar.m" +#line 1615 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr62; goto st0; tr60: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st36; st36: if ( ++p == pe ) goto _test_eof36; case 36: -#line 1579 "mod/box/memcached-grammar.m" +#line 1627 "mod/box/memcached-grammar.m" switch( (*p) ) { case 32: goto st36; case 78: goto st37; @@ -1682,7 +1730,7 @@ case 47: goto st0; goto tr76; tr76: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -1699,7 +1747,7 @@ st48: if ( ++p == pe ) goto _test_eof48; case 48: -#line 1703 "mod/box/memcached-grammar.m" +#line 1751 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st49; goto st0; @@ -1713,49 +1761,49 @@ case 49: goto tr78; goto st0; tr78: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st50; st50: if ( ++p == pe ) goto _test_eof50; case 50: -#line 1724 "mod/box/memcached-grammar.m" +#line 1772 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr79; if ( 48 <= (*p) && (*p) <= 57 ) goto st50; goto st0; tr79: -#line 243 "mod/box/memcached-grammar.rl" +#line 246 "mod/box/memcached-grammar.rl" {flags = natoq(fstart, p);} goto st51; st51: if ( ++p == pe ) goto _test_eof51; case 51: -#line 1738 "mod/box/memcached-grammar.m" +#line 1786 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st51; if ( 48 <= (*p) && (*p) <= 57 ) goto tr82; goto st0; tr82: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st52; st52: if ( ++p == pe ) goto _test_eof52; case 52: -#line 1752 "mod/box/memcached-grammar.m" +#line 1800 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr83; if ( 48 <= (*p) && (*p) <= 57 ) goto st52; goto st0; tr83: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -1766,49 +1814,49 @@ st53: if ( ++p == pe ) goto _test_eof53; case 53: -#line 1770 "mod/box/memcached-grammar.m" +#line 1818 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st53; if ( 48 <= (*p) && (*p) <= 57 ) goto tr86; goto st0; tr86: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st54; st54: if ( ++p == pe ) goto _test_eof54; case 54: -#line 1784 "mod/box/memcached-grammar.m" +#line 1832 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr87; if ( 48 <= (*p) && (*p) <= 57 ) goto st54; goto st0; tr87: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st55; st55: if ( ++p == pe ) goto _test_eof55; case 55: -#line 1798 "mod/box/memcached-grammar.m" +#line 1846 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st55; if ( 48 <= (*p) && (*p) <= 57 ) goto tr90; goto st0; tr90: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st56; st56: if ( ++p == pe ) goto _test_eof56; case 56: -#line 1812 "mod/box/memcached-grammar.m" +#line 1860 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr91; case 13: goto tr92; @@ -1818,30 +1866,30 @@ case 56: goto st56; goto st0; tr106: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st57; tr92: -#line 245 "mod/box/memcached-grammar.rl" +#line 248 "mod/box/memcached-grammar.rl" {cas = natoq(fstart, p);} goto st57; st57: if ( ++p == pe ) goto _test_eof57; case 57: -#line 1833 "mod/box/memcached-grammar.m" +#line 1881 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr95; goto st0; tr93: -#line 245 "mod/box/memcached-grammar.rl" +#line 248 "mod/box/memcached-grammar.rl" {cas = natoq(fstart, p);} goto st58; st58: if ( ++p == pe ) goto _test_eof58; case 58: -#line 1845 "mod/box/memcached-grammar.m" +#line 1893 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -1915,14 +1963,14 @@ case 65: } goto st0; tr107: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st66; st66: if ( ++p == pe ) goto _test_eof66; case 66: -#line 1926 "mod/box/memcached-grammar.m" +#line 1974 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -1966,18 +2014,18 @@ case 70: goto tr113; goto st0; tr113: -#line 291 "mod/box/memcached-grammar.rl" +#line 297 "mod/box/memcached-grammar.rl" {incr_sign = -1;} goto st71; tr202: -#line 290 "mod/box/memcached-grammar.rl" +#line 296 "mod/box/memcached-grammar.rl" {incr_sign = 1; } goto st71; st71: if ( ++p == pe ) goto _test_eof71; case 71: -#line 1981 "mod/box/memcached-grammar.m" +#line 2029 "mod/box/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st71; @@ -1986,7 +2034,7 @@ case 71: goto st0; goto tr114; tr114: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2003,7 +2051,7 @@ st72: if ( ++p == pe ) goto _test_eof72; case 72: -#line 2007 "mod/box/memcached-grammar.m" +#line 2055 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st73; goto st0; @@ -2017,14 +2065,14 @@ case 73: goto tr117; goto st0; tr117: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st74; st74: if ( ++p == pe ) goto _test_eof74; case 74: -#line 2028 "mod/box/memcached-grammar.m" +#line 2076 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr118; case 13: goto tr119; @@ -2034,30 +2082,30 @@ case 74: goto st74; goto st0; tr133: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st75; tr119: -#line 246 "mod/box/memcached-grammar.rl" +#line 249 "mod/box/memcached-grammar.rl" {incr = natoq(fstart, p);} goto st75; st75: if ( ++p == pe ) goto _test_eof75; case 75: -#line 2049 "mod/box/memcached-grammar.m" +#line 2097 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr122; goto st0; tr120: -#line 246 "mod/box/memcached-grammar.rl" +#line 249 "mod/box/memcached-grammar.rl" {incr = natoq(fstart, p);} goto st76; st76: if ( ++p == pe ) goto _test_eof76; case 76: -#line 2061 "mod/box/memcached-grammar.m" +#line 2109 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2131,14 +2179,14 @@ case 83: } goto st0; tr134: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st84; st84: if ( ++p == pe ) goto _test_eof84; case 84: -#line 2142 "mod/box/memcached-grammar.m" +#line 2190 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2191,7 +2239,7 @@ case 89: goto st0; goto tr140; tr140: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2208,7 +2256,7 @@ st90: if ( ++p == pe ) goto _test_eof90; case 90: -#line 2212 "mod/box/memcached-grammar.m" +#line 2260 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2216,7 +2264,7 @@ case 90: } goto st0; tr147: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2224,14 +2272,14 @@ tr147: } goto st91; tr158: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st91; st91: if ( ++p == pe ) goto _test_eof91; case 91: -#line 2235 "mod/box/memcached-grammar.m" +#line 2283 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr141; goto st0; @@ -2250,14 +2298,14 @@ case 92: goto tr144; goto st0; tr144: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st93; st93: if ( ++p == pe ) goto _test_eof93; case 93: -#line 2261 "mod/box/memcached-grammar.m" +#line 2309 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr146; case 13: goto tr147; @@ -2267,7 +2315,7 @@ case 93: goto st93; goto st0; tr148: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2278,7 +2326,7 @@ st94: if ( ++p == pe ) goto _test_eof94; case 94: -#line 2282 "mod/box/memcached-grammar.m" +#line 2330 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2352,14 +2400,14 @@ case 101: } goto st0; tr159: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st102; st102: if ( ++p == pe ) goto _test_eof102; case 102: -#line 2363 "mod/box/memcached-grammar.m" +#line 2411 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2447,18 +2495,18 @@ case 111: } goto st0; tr186: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st112; tr175: -#line 247 "mod/box/memcached-grammar.rl" +#line 250 "mod/box/memcached-grammar.rl" {flush_delay = natoq(fstart, p);} goto st112; st112: if ( ++p == pe ) goto _test_eof112; case 112: -#line 2462 "mod/box/memcached-grammar.m" +#line 2510 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr169; goto st0; @@ -2477,14 +2525,14 @@ case 113: goto tr172; goto st0; tr172: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st114; st114: if ( ++p == pe ) goto _test_eof114; case 114: -#line 2488 "mod/box/memcached-grammar.m" +#line 2536 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr174; case 13: goto tr175; @@ -2494,14 +2542,14 @@ case 114: goto st114; goto st0; tr176: -#line 247 "mod/box/memcached-grammar.rl" +#line 250 "mod/box/memcached-grammar.rl" {flush_delay = natoq(fstart, p);} goto st115; st115: if ( ++p == pe ) goto _test_eof115; case 115: -#line 2505 "mod/box/memcached-grammar.m" +#line 2553 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2575,14 +2623,14 @@ case 122: } goto st0; tr187: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st123; st123: if ( ++p == pe ) goto _test_eof123; case 123: -#line 2586 "mod/box/memcached-grammar.m" +#line 2634 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2618,18 +2666,18 @@ case 126: } goto st0; tr191: -#line 287 "mod/box/memcached-grammar.rl" +#line 293 "mod/box/memcached-grammar.rl" {show_cas = false;} goto st127; tr198: -#line 288 "mod/box/memcached-grammar.rl" +#line 294 "mod/box/memcached-grammar.rl" {show_cas = true;} goto st127; st127: if ( ++p == pe ) goto _test_eof127; case 127: -#line 2633 "mod/box/memcached-grammar.m" +#line 2681 "mod/box/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st127; @@ -2638,7 +2686,7 @@ case 127: goto st0; goto tr193; tr193: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2655,7 +2703,7 @@ st128: if ( ++p == pe ) goto _test_eof128; case 128: -#line 2659 "mod/box/memcached-grammar.m" +#line 2707 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr195; case 13: goto st129; @@ -2898,7 +2946,7 @@ case 155: goto st0; goto tr222; tr222: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -2915,7 +2963,7 @@ st156: if ( ++p == pe ) goto _test_eof156; case 156: -#line 2919 "mod/box/memcached-grammar.m" +#line 2967 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st157; goto st0; @@ -2929,49 +2977,49 @@ case 157: goto tr224; goto st0; tr224: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st158; st158: if ( ++p == pe ) goto _test_eof158; case 158: -#line 2940 "mod/box/memcached-grammar.m" +#line 2988 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr225; if ( 48 <= (*p) && (*p) <= 57 ) goto st158; goto st0; tr225: -#line 243 "mod/box/memcached-grammar.rl" +#line 246 "mod/box/memcached-grammar.rl" {flags = natoq(fstart, p);} goto st159; st159: if ( ++p == pe ) goto _test_eof159; case 159: -#line 2954 "mod/box/memcached-grammar.m" +#line 3002 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st159; if ( 48 <= (*p) && (*p) <= 57 ) goto tr228; goto st0; tr228: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st160; st160: if ( ++p == pe ) goto _test_eof160; case 160: -#line 2968 "mod/box/memcached-grammar.m" +#line 3016 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr229; if ( 48 <= (*p) && (*p) <= 57 ) goto st160; goto st0; tr229: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -2982,21 +3030,21 @@ st161: if ( ++p == pe ) goto _test_eof161; case 161: -#line 2986 "mod/box/memcached-grammar.m" +#line 3034 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st161; if ( 48 <= (*p) && (*p) <= 57 ) goto tr232; goto st0; tr232: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st162; st162: if ( ++p == pe ) goto _test_eof162; case 162: -#line 3000 "mod/box/memcached-grammar.m" +#line 3048 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr233; case 13: goto tr234; @@ -3006,30 +3054,30 @@ case 162: goto st162; goto st0; tr234: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st163; tr247: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st163; st163: if ( ++p == pe ) goto _test_eof163; case 163: -#line 3021 "mod/box/memcached-grammar.m" +#line 3069 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr237; goto st0; tr235: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st164; st164: if ( ++p == pe ) goto _test_eof164; case 164: -#line 3033 "mod/box/memcached-grammar.m" +#line 3081 "mod/box/memcached-grammar.m" switch( (*p) ) { case 32: goto st164; case 78: goto st165; @@ -3138,7 +3186,7 @@ case 175: goto st0; goto tr252; tr252: -#line 220 "mod/box/memcached-grammar.rl" +#line 223 "mod/box/memcached-grammar.rl" { fstart = p; for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++); @@ -3155,7 +3203,7 @@ st176: if ( ++p == pe ) goto _test_eof176; case 176: -#line 3159 "mod/box/memcached-grammar.m" +#line 3207 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st177; goto st0; @@ -3169,49 +3217,49 @@ case 177: goto tr254; goto st0; tr254: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st178; st178: if ( ++p == pe ) goto _test_eof178; case 178: -#line 3180 "mod/box/memcached-grammar.m" +#line 3228 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr255; if ( 48 <= (*p) && (*p) <= 57 ) goto st178; goto st0; tr255: -#line 243 "mod/box/memcached-grammar.rl" +#line 246 "mod/box/memcached-grammar.rl" {flags = natoq(fstart, p);} goto st179; st179: if ( ++p == pe ) goto _test_eof179; case 179: -#line 3194 "mod/box/memcached-grammar.m" +#line 3242 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st179; if ( 48 <= (*p) && (*p) <= 57 ) goto tr258; goto st0; tr258: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st180; st180: if ( ++p == pe ) goto _test_eof180; case 180: -#line 3208 "mod/box/memcached-grammar.m" +#line 3256 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto tr259; if ( 48 <= (*p) && (*p) <= 57 ) goto st180; goto st0; tr259: -#line 236 "mod/box/memcached-grammar.rl" +#line 239 "mod/box/memcached-grammar.rl" { exptime = natoq(fstart, p); if (exptime > 0 && exptime <= 60*60*24*30) @@ -3222,21 +3270,21 @@ st181: if ( ++p == pe ) goto _test_eof181; case 181: -#line 3226 "mod/box/memcached-grammar.m" +#line 3274 "mod/box/memcached-grammar.m" if ( (*p) == 32 ) goto st181; if ( 48 <= (*p) && (*p) <= 57 ) goto tr262; goto st0; tr262: -#line 219 "mod/box/memcached-grammar.rl" +#line 222 "mod/box/memcached-grammar.rl" { fstart = p; } goto st182; st182: if ( ++p == pe ) goto _test_eof182; case 182: -#line 3240 "mod/box/memcached-grammar.m" +#line 3288 "mod/box/memcached-grammar.m" switch( (*p) ) { case 10: goto tr263; case 13: goto tr264; @@ -3246,30 +3294,30 @@ case 182: goto st182; goto st0; tr264: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st183; tr277: -#line 276 "mod/box/memcached-grammar.rl" +#line 282 "mod/box/memcached-grammar.rl" { noreply = true; } goto st183; st183: if ( ++p == pe ) goto _test_eof183; case 183: -#line 3261 "mod/box/memcached-grammar.m" +#line 3309 "mod/box/memcached-grammar.m" if ( (*p) == 10 ) goto tr267; goto st0; tr265: -#line 244 "mod/box/memcached-grammar.rl" +#line 247 "mod/box/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st184; st184: if ( ++p == pe ) goto _test_eof184; case 184: -#line 3273 "mod/box/memcached-grammar.m" +#line 3321 "mod/box/memcached-grammar.m" switch( (*p) ) { case 32: goto st184; case 78: goto st185; @@ -3584,7 +3632,7 @@ case 196: _out: {} } -#line 301 "mod/box/memcached-grammar.rl" +#line 307 "mod/box/memcached-grammar.rl" if (!done) { @@ -3592,22 +3640,21 @@ case 196: if (pe - p > (1 << 20)) { exit: say_warn("memcached proto error"); - iov_add("ERROR\r\n", 7); + obuf_dup(out, "ERROR\r\n", 7); stats.bytes_written += 7; return -1; } char *r; if ((r = memmem(p, pe - p, "\r\n", 2)) != NULL) { - tbuf_peek(&fiber->rbuf, r + 2 - (char *)fiber->rbuf.data); - iov_add("CLIENT_ERROR bad command line format\r\n", 38); + in->pos = r + 2; + obuf_dup(out, "CLIENT_ERROR bad command line format\r\n", 38); return 1; } return 0; } if (noreply) { - fiber->iov_cnt = saved_iov_cnt; - fiber->iov.size = saved_iov_cnt * sizeof(struct iovec); + obuf_rollback_to_svp(out, &obuf_svp); } return 1; } diff --git a/mod/box/memcached-grammar.rl b/mod/box/memcached-grammar.rl index cbcad97260..88e6bbd7c9 100644 --- a/mod/box/memcached-grammar.rl +++ b/mod/box/memcached-grammar.rl @@ -33,11 +33,11 @@ }%% static int __attribute__((noinline)) -memcached_dispatch(struct coio *coio) +memcached_dispatch(struct coio *coio, struct iobuf *iobuf) { int cs; - u8 *p, *pe; - u8 *fstart; + char *p, *pe; + char *fstart; struct tbuf *keys = tbuf_alloc(fiber->gc_pool); void *key; bool append, show_cas; @@ -45,14 +45,17 @@ memcached_dispatch(struct coio *coio) u64 cas, incr; u32 flags, exptime, bytes; bool noreply = false; - u8 *data = NULL; + char *data = NULL; bool done = false; - size_t saved_iov_cnt = fiber->iov_cnt; uintptr_t flush_delay = 0; size_t keys_count = 0; + struct ibuf *in = &iobuf->in; + struct obuf *out = &iobuf->out; + /* Savepoint for 'noreply' */ + struct obuf_svp obuf_svp = obuf_create_svp(out); - p = fiber->rbuf.data; - pe = fiber->rbuf.data + fiber->rbuf.size; + p = in->pos; + pe = in->end; say_debug("memcached_dispatch '%.*s'", MIN((int)(pe - p), 40) , p); @@ -66,7 +69,7 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple != NULL && !expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } @@ -75,7 +78,7 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); else STORE; } @@ -84,9 +87,9 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || expired(tuple)) - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); else if (meta(tuple)->cas != cas) - iov_add("EXISTS\r\n", 8); + obuf_dup(out, "EXISTS\r\n", 8); else STORE; } @@ -99,7 +102,7 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST) { - iov_add("NOT_STORED\r\n", 12); + obuf_dup(out, "NOT_STORED\r\n", 12); } else { value = tuple_field(tuple, 3); value_len = load_varint32(&value); @@ -128,7 +131,7 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { m = meta(tuple); field = tuple_field(tuple, 3); @@ -158,16 +161,16 @@ memcached_dispatch(struct coio *coio) @try { store(key, exptime, flags, bytes, data); stats.total_items++; - iov_add(b->data, b->size); - iov_add("\r\n", 2); + obuf_dup(out, b->data, b->size); + obuf_dup(out, "\r\n", 2); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } else { - iov_add("CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); + obuf_dup(out, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 62); } } @@ -177,39 +180,39 @@ memcached_dispatch(struct coio *coio) key = read_field(keys); struct tuple *tuple = find(key); if (tuple == NULL || tuple->flags & GHOST || expired(tuple)) { - iov_add("NOT_FOUND\r\n", 11); + obuf_dup(out, "NOT_FOUND\r\n", 11); } else { @try { delete(key); - iov_add("DELETED\r\n", 9); + obuf_dup(out, "DELETED\r\n", 9); } @catch (ClientError *e) { - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } } action get { @try { - memcached_get(keys_count, keys, show_cas); + memcached_get(out, keys_count, keys, show_cas); } @catch (ClientError *e) { - iov_reset(); - iov_add("SERVER_ERROR ", 13); - iov_add(e->errmsg, strlen(e->errmsg)); - iov_add("\r\n", 2); + obuf_rollback_to_svp(out, &obuf_svp); + obuf_dup(out, "SERVER_ERROR ", 13); + obuf_dup(out, e->errmsg, strlen(e->errmsg)); + obuf_dup(out, "\r\n", 2); } } action flush_all { struct fiber *f = fiber_create("flush_all", flush_all); fiber_call(f, flush_delay); - iov_add("OK\r\n", 4); + obuf_dup(out, "OK\r\n", 4); } action stats { - print_stats(); + print_stats(out); } action quit { @@ -247,14 +250,17 @@ memcached_dispatch(struct coio *coio) flush_delay = digit+ >fstart %{flush_delay = natoq(fstart, p);}; action read_data { - size_t parsed = p - (u8 *)fiber->rbuf.data; - while (fiber->rbuf.size - parsed < bytes + 2) { - if (coio_bread(coio, &fiber->rbuf, bytes + 2 - (pe - p)) <= 0) + size_t parsed = p - in->pos; + while (ibuf_size(in) - parsed < bytes + 2) { + if (coio_bread(coio, in, bytes + 2 - (pe - p)) <= 0) return -1; } - - p = fiber->rbuf.data + parsed; - pe = fiber->rbuf.data + fiber->rbuf.size; + /* + * Buffered read may have reallocated the + * buffer. + */ + p = in->pos + parsed; + pe = in->end; data = p; @@ -267,8 +273,8 @@ memcached_dispatch(struct coio *coio) action done { done = true; - stats.bytes_read += p - (u8 *)fiber->rbuf.data; - tbuf_peek(&fiber->rbuf, p - (u8 *)fiber->rbuf.data); + stats.bytes_read += p - in->pos; + in->pos = p; } eol = ("\r\n" | "\n") @{ p++; }; @@ -305,22 +311,21 @@ memcached_dispatch(struct coio *coio) if (pe - p > (1 << 20)) { exit: say_warn("memcached proto error"); - iov_add("ERROR\r\n", 7); + obuf_dup(out, "ERROR\r\n", 7); stats.bytes_written += 7; return -1; } char *r; if ((r = memmem(p, pe - p, "\r\n", 2)) != NULL) { - tbuf_peek(&fiber->rbuf, r + 2 - (char *)fiber->rbuf.data); - iov_add("CLIENT_ERROR bad command line format\r\n", 38); + in->pos = r + 2; + obuf_dup(out, "CLIENT_ERROR bad command line format\r\n", 38); return 1; } return 0; } if (noreply) { - fiber->iov_cnt = saved_iov_cnt; - fiber->iov.size = saved_iov_cnt * sizeof(struct iovec); + obuf_rollback_to_svp(out, &obuf_svp); } return 1; } diff --git a/mod/box/memcached.m b/mod/box/memcached.m index 9c1fdc4bf2..50aa60cd05 100644 --- a/mod/box/memcached.m +++ b/mod/box/memcached.m @@ -71,16 +71,18 @@ struct meta { } __packed__; static u64 -natoq(const u8 *start, const u8 *end) +natoq(const char *start, const char *end) { u64 num = 0; - while (start < end) - num = num * 10 + (*start++ - '0'); + while (start < end) { + u8 code = *start++; + num = num * 10 + (code - '0'); + } return num; } static void -store(void *key, u32 exptime, u32 flags, u32 bytes, u8 *data) +store(void *key, u32 exptime, u32 flags, u32 bytes, const char *data) { u32 box_flags = 0; u32 field_count = 4; @@ -111,7 +113,7 @@ store(void *key, u32 exptime, u32 flags, u32 bytes, u8 *data) int key_len = load_varint32(&key); say_debug("memcached/store key:(%i)'%.*s' exptime:%"PRIu32" flags:%"PRIu32" cas:%"PRIu64, - key_len, key_len, (u8 *)key, exptime, flags, cas); + key_len, key_len, (char*) key, exptime, flags, cas); /* * Use a box dispatch wrapper which handles correctly * read-only/read-write modes. @@ -191,45 +193,44 @@ salloc_stat_memcached_cb(const struct slab_class_stats *cstat, void *cb_ctx) } static void -print_stats() +print_stats(struct obuf *out) { - struct tbuf *out = tbuf_alloc(fiber->gc_pool); + struct tbuf *buf = tbuf_alloc(fiber->gc_pool); struct salloc_stat_memcached_cb_ctx memstats; memstats.bytes_used = memstats.items = 0; salloc_stat(salloc_stat_memcached_cb, NULL, &memstats); - tbuf_printf(out, "STAT pid %"PRIu32"\r\n", (u32)getpid()); - tbuf_printf(out, "STAT uptime %"PRIu32"\r\n", (u32)tarantool_uptime()); - tbuf_printf(out, "STAT time %"PRIu32"\r\n", (u32)ev_now()); - tbuf_printf(out, "STAT version 1.2.5 (tarantool/box)\r\n"); - tbuf_printf(out, "STAT pointer_size %"PRI_SZ"\r\n", sizeof(void *)*8); - tbuf_printf(out, "STAT curr_items %"PRIu64"\r\n", memstats.items); - tbuf_printf(out, "STAT total_items %"PRIu64"\r\n", stats.total_items); - tbuf_printf(out, "STAT bytes %"PRIu64"\r\n", memstats.bytes_used); - tbuf_printf(out, "STAT curr_connections %"PRIu32"\r\n", stats.curr_connections); - tbuf_printf(out, "STAT total_connections %"PRIu32"\r\n", stats.total_connections); - tbuf_printf(out, "STAT connection_structures %"PRIu32"\r\n", stats.curr_connections); /* lie a bit */ - tbuf_printf(out, "STAT cmd_get %"PRIu64"\r\n", stats.cmd_get); - tbuf_printf(out, "STAT cmd_set %"PRIu64"\r\n", stats.cmd_set); - tbuf_printf(out, "STAT get_hits %"PRIu64"\r\n", stats.get_hits); - tbuf_printf(out, "STAT get_misses %"PRIu64"\r\n", stats.get_misses); - tbuf_printf(out, "STAT evictions %"PRIu64"\r\n", stats.evictions); - tbuf_printf(out, "STAT bytes_read %"PRIu64"\r\n", stats.bytes_read); - tbuf_printf(out, "STAT bytes_written %"PRIu64"\r\n", stats.bytes_written); - tbuf_printf(out, "STAT limit_maxbytes %"PRIu64"\r\n", (u64)(cfg.slab_alloc_arena * (1 << 30))); - tbuf_printf(out, "STAT threads 1\r\n"); - tbuf_printf(out, "END\r\n"); - iov_add(out->data, out->size); + tbuf_printf(buf, "STAT pid %"PRIu32"\r\n", (u32)getpid()); + tbuf_printf(buf, "STAT uptime %"PRIu32"\r\n", (u32)tarantool_uptime()); + tbuf_printf(buf, "STAT time %"PRIu32"\r\n", (u32)ev_now()); + tbuf_printf(buf, "STAT version 1.2.5 (tarantool/box)\r\n"); + tbuf_printf(buf, "STAT pointer_size %"PRI_SZ"\r\n", sizeof(void *)*8); + tbuf_printf(buf, "STAT curr_items %"PRIu64"\r\n", memstats.items); + tbuf_printf(buf, "STAT total_items %"PRIu64"\r\n", stats.total_items); + tbuf_printf(buf, "STAT bytes %"PRIu64"\r\n", memstats.bytes_used); + tbuf_printf(buf, "STAT curr_connections %"PRIu32"\r\n", stats.curr_connections); + tbuf_printf(buf, "STAT total_connections %"PRIu32"\r\n", stats.total_connections); + tbuf_printf(buf, "STAT connection_structures %"PRIu32"\r\n", stats.curr_connections); /* lie a bit */ + tbuf_printf(buf, "STAT cmd_get %"PRIu64"\r\n", stats.cmd_get); + tbuf_printf(buf, "STAT cmd_set %"PRIu64"\r\n", stats.cmd_set); + tbuf_printf(buf, "STAT get_hits %"PRIu64"\r\n", stats.get_hits); + tbuf_printf(buf, "STAT get_misses %"PRIu64"\r\n", stats.get_misses); + tbuf_printf(buf, "STAT evictions %"PRIu64"\r\n", stats.evictions); + tbuf_printf(buf, "STAT bytes_read %"PRIu64"\r\n", stats.bytes_read); + tbuf_printf(buf, "STAT bytes_written %"PRIu64"\r\n", stats.bytes_written); + tbuf_printf(buf, "STAT limit_maxbytes %"PRIu64"\r\n", (u64)(cfg.slab_alloc_arena * (1 << 30))); + tbuf_printf(buf, "STAT threads 1\r\n"); + tbuf_printf(buf, "END\r\n"); + obuf_dup(out, buf->data, buf->size); } -void memcached_get(size_t keys_count, struct tbuf *keys, +void memcached_get(struct obuf *out, size_t keys_count, struct tbuf *keys, bool show_cas) { stat_collect(stat_base, MEMC_GET, 1); stats.cmd_get++; say_debug("ensuring space for %"PRI_SZ" keys", keys_count); - iov_ensure(keys_count * 5 + 1); while (keys_count-- > 0) { struct tuple *tuple; struct meta *m; @@ -279,23 +280,21 @@ void memcached_get(size_t keys_count, struct tbuf *keys, stats.get_hits++; stat_collect(stat_base, MEMC_GET_HIT, 1); - iov_ref_tuple(tuple); - if (show_cas) { struct tbuf *b = tbuf_alloc(fiber->gc_pool); - tbuf_printf(b, "VALUE %.*s %"PRIu32" %"PRIu32" %"PRIu64"\r\n", key_len, (u8 *)key, m->flags, value_len, m->cas); - iov_add_unsafe(b->data, b->size); + tbuf_printf(b, "VALUE %.*s %"PRIu32" %"PRIu32" %"PRIu64"\r\n", key_len, (char*) key, m->flags, value_len, m->cas); + obuf_dup(out, b->data, b->size); stats.bytes_written += b->size; } else { - iov_add_unsafe("VALUE ", 6); - iov_add_unsafe(key, key_len); - iov_add_unsafe(suffix, suffix_len); + obuf_dup(out, "VALUE ", 6); + obuf_dup(out, key, key_len); + obuf_dup(out, suffix, suffix_len); } - iov_add_unsafe(value, value_len); - iov_add_unsafe("\r\n", 2); + obuf_dup(out, value, value_len); + obuf_dup(out, "\r\n", 2); stats.bytes_written += value_len + 2; } - iov_add_unsafe("END\r\n", 5); + obuf_dup(out, "END\r\n", 5); stats.bytes_written += 5; } @@ -317,17 +316,17 @@ flush_all(va_list ap) do { \ stats.cmd_set++; \ if (bytes > (1<<20)) { \ - iov_add("SERVER_ERROR object too large for cache\r\n", 41); \ + obuf_dup(out, "SERVER_ERROR object too large for cache\r\n", 41);\ } else { \ @try { \ store(key, exptime, flags, bytes, data); \ stats.total_items++; \ - iov_add("STORED\r\n", 8); \ + obuf_dup(out, "STORED\r\n", 8); \ } \ @catch (ClientError *e) { \ - iov_add("SERVER_ERROR ", 13); \ - iov_add(e->errmsg, strlen(e->errmsg)); \ - iov_add("\r\n", 2); \ + obuf_dup(out, "SERVER_ERROR ", 13); \ + obuf_dup(out, e->errmsg, strlen(e->errmsg)); \ + obuf_dup(out, "\r\n", 2); \ } \ } \ } while (0) @@ -335,19 +334,20 @@ do { \ #include "memcached-grammar.m" void -memcached_loop(struct coio *coio) +memcached_loop(struct coio *coio, struct iobuf *iobuf) { int rc; int bytes_written; int batch_count; + struct ibuf *in = &iobuf->in; for (;;) { batch_count = 0; - if (coio_bread(coio, &fiber->rbuf, 1) <= 0) + if (coio_bread(coio, in, 1) <= 0) return; dispatch: - rc = memcached_dispatch(coio); + rc = memcached_dispatch(coio, iobuf); if (rc < 0) { say_debug("negative dispatch, closing connection"); return; @@ -359,16 +359,15 @@ memcached_loop(struct coio *coio) if (rc == 1) { batch_count++; /* some unparsed commands remain and batch count less than 20 */ - if (fiber->rbuf.size > 0 && batch_count < 20) + if (ibuf_size(in) > 0 && batch_count < 20) goto dispatch; } - bytes_written = iov_flush(coio); - - stats.bytes_written += bytes_written; + bytes_written = iobuf_flush(iobuf, coio); fiber_gc(); + stats.bytes_written += bytes_written; - if (rc == 1 && fiber->rbuf.size > 0) { + if (rc == 1 && ibuf_size(in) > 0) { batch_count = 0; goto dispatch; } @@ -379,21 +378,22 @@ memcached_loop(struct coio *coio) void memcached_handler(va_list ap) { struct coio coio = va_arg(ap, struct coio); + struct iobuf *iobuf = va_arg(ap, struct iobuf *); stats.total_connections++; stats.curr_connections++; @try { - memcached_loop(&coio); - iov_flush(&coio); + memcached_loop(&coio, iobuf); + iobuf_flush(iobuf, &coio); } @catch (FiberCancelException *e) { @throw; } @catch (tnt_Exception *e) { [e log]; } @finally { - iov_reset(); fiber_sleep(0.01); stats.curr_connections--; coio_close(&coio); + iobuf_destroy(iobuf); } } diff --git a/mod/box/port.h b/mod/box/port.h index 2e43ec0091..bb5ef9f16e 100644 --- a/mod/box/port.h +++ b/mod/box/port.h @@ -45,13 +45,6 @@ struct port struct port_vtab *vtab; }; -/** - * A hack to keep tuples alive until iov_flush(fiber->iovec). - * Is internal to port_iproto implementation, but is also - * used in memcached.m, which doesn't use fiber->iovec. - */ -void iov_ref_tuple(struct tuple *tuple); - static inline void port_eof(struct port *port) { @@ -74,7 +67,9 @@ port_null_eof(struct port *port __attribute__((unused))); */ extern struct port port_null; +struct obuf; + struct port * -port_iproto_create(); +port_iproto_create(struct obuf *buf); #endif /* INCLUDES_TARANTOOL_BOX_PORT_H */ diff --git a/mod/box/port.m b/mod/box/port.m index 987c25abb8..fbf5b5aa34 100644 --- a/mod/box/port.m +++ b/mod/box/port.m @@ -27,18 +27,10 @@ * SUCH DAMAGE. */ #include "port.h" -#include <pickle.h> -#include <fiber.h> -#include <tarantool_lua.h> #include "tuple.h" -#include "box_lua.h" -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -#include "lj_obj.h" -#include "lj_ctype.h" -#include "lj_cdata.h" -#include "lj_cconv.h" +#include "iobuf.h" +#include "fiber.h" +#include "request.h" /* For tuples of size below this threshold, when sending a tuple @@ -53,19 +45,6 @@ */ const int BOX_REF_THRESHOLD = 8196; -static void -tuple_unref(void *tuple) -{ - tuple_ref((struct tuple *) tuple, -1); -} - -void -iov_ref_tuple(struct tuple *tuple) -{ - tuple_ref(tuple, 1); - fiber_register_cleanup(tuple_unref, tuple); -} - void port_null_eof(struct port *port __attribute__((unused))) { @@ -90,8 +69,10 @@ struct port port_null = { struct port_iproto { struct port_vtab *vtab; + struct obuf *buf; /** Number of found tuples. */ u32 found; + void *pfound; }; static inline struct port_iproto * @@ -106,7 +87,9 @@ port_iproto_eof(struct port *ptr) struct port_iproto *port = port_iproto(ptr); /* found == 0 means add_tuple wasn't called at all. */ if (port->found == 0) - iov_dup(&port->found, sizeof(port->found)); + obuf_dup(port->buf, &port->found, sizeof(port->found)); + else + memcpy(port->pfound, &port->found, sizeof(port->found)); } static void @@ -115,17 +98,10 @@ port_iproto_add_tuple(struct port *ptr, struct tuple *tuple, u32 flags) struct port_iproto *port = port_iproto(ptr); if (++port->found == 1) { /* Found the first tuple, add tuple count. */ - iov_add(&port->found, sizeof(port->found)); + port->pfound = obuf_book(port->buf, sizeof(port->found)); } if (flags & BOX_RETURN_TUPLE) { - size_t len = tuple_len(tuple); - - if (len > BOX_REF_THRESHOLD) { - iov_ref_tuple(tuple); - iov_add(&tuple->bsize, len); - } else { - iov_dup(&tuple->bsize, len); - } + obuf_dup(port->buf, &tuple->bsize, tuple_len(tuple)); } } @@ -135,11 +111,13 @@ static struct port_vtab port_iproto_vtab = { }; struct port * -port_iproto_create() +port_iproto_create(struct obuf *buf) { struct port_iproto *port = palloc(fiber->gc_pool, sizeof(struct port_iproto)); port->vtab = &port_iproto_vtab; + port->buf = buf; port->found = 0; + port->pfound = NULL; return (struct port *) port; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf0c4ec67d..49000643c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ set (common_sources sio.m evio.m coio.m + iobuf.m coio_buf.m salloc.m pickle.m diff --git a/src/admin.m b/src/admin.m index cfc9d66955..8ccc370283 100644 --- a/src/admin.m +++ b/src/admin.m @@ -198,8 +198,9 @@ show_stat(struct tbuf *buf) } static int -admin_dispatch(struct coio *coio, lua_State *L) +admin_dispatch(struct coio *coio, struct iobuf *iobuf, lua_State *L) { + struct ibuf *in = &iobuf->in; struct tbuf *out = tbuf_alloc(fiber->gc_pool); struct tbuf *err = tbuf_alloc(fiber->gc_pool); int cs; @@ -207,21 +208,21 @@ admin_dispatch(struct coio *coio, lua_State *L) char *strstart, *strend; bool state; - while ((pe = memchr(fiber->rbuf.data, '\n', fiber->rbuf.size)) == NULL) { - if (coio_bread(coio, &fiber->rbuf, 1) <= 0) + while ((pe = memchr(in->pos, '\n', in->end - in->pos)) == NULL) { + if (coio_bread(coio, in, 1) <= 0) return -1; } pe++; - p = fiber->rbuf.data; + p = in->pos; -#line 220 "src/admin.m" +#line 221 "src/admin.m" { cs = admin_start; } -#line 225 "src/admin.m" +#line 226 "src/admin.m" { if ( p == pe ) goto _test_eof; @@ -284,15 +285,15 @@ case 6: } goto st0; tr13: -#line 319 "src/admin.rl" +#line 320 "src/admin.rl" {slab_validate(); ok(out);} goto st135; tr20: -#line 307 "src/admin.rl" - {return 0;} +#line 308 "src/admin.rl" + {return -1;} goto st135; tr25: -#line 234 "src/admin.rl" +#line 235 "src/admin.rl" { start(out); tbuf_append(out, help, strlen(help)); @@ -300,9 +301,9 @@ tr25: } goto st135; tr36: -#line 293 "src/admin.rl" +#line 294 "src/admin.rl" {strend = p;} -#line 240 "src/admin.rl" +#line 241 "src/admin.rl" { strstart[strend-strstart]='\0'; start(out); @@ -311,7 +312,7 @@ tr36: } goto st135; tr43: -#line 247 "src/admin.rl" +#line 248 "src/admin.rl" { if (reload_cfg(err)) fail(out, err); @@ -320,11 +321,11 @@ tr43: } goto st135; tr67: -#line 317 "src/admin.rl" +#line 318 "src/admin.rl" {coredump(60); ok(out);} goto st135; tr76: -#line 254 "src/admin.rl" +#line 255 "src/admin.rl" { int ret = snapshot(NULL, 0); @@ -339,9 +340,9 @@ tr76: } goto st135; tr98: -#line 303 "src/admin.rl" +#line 304 "src/admin.rl" { state = false; } -#line 267 "src/admin.rl" +#line 268 "src/admin.rl" { strstart[strend-strstart] = '\0'; if (errinj_set_byname(strstart, state)) { @@ -353,9 +354,9 @@ tr98: } goto st135; tr101: -#line 302 "src/admin.rl" +#line 303 "src/admin.rl" { state = true; } -#line 267 "src/admin.rl" +#line 268 "src/admin.rl" { strstart[strend-strstart] = '\0'; if (errinj_set_byname(strstart, state)) { @@ -367,7 +368,7 @@ tr101: } goto st135; tr117: -#line 210 "src/admin.rl" +#line 211 "src/admin.rl" { tarantool_cfg_iterator_t *i; char *key, *value; @@ -387,15 +388,15 @@ tr117: } goto st135; tr131: -#line 310 "src/admin.rl" +#line 311 "src/admin.rl" {start(out); fiber_info(out); end(out);} goto st135; tr137: -#line 309 "src/admin.rl" +#line 310 "src/admin.rl" {start(out); tarantool_info(out); end(out);} goto st135; tr146: -#line 228 "src/admin.rl" +#line 229 "src/admin.rl" { start(out); errinj_info(out); @@ -403,33 +404,33 @@ tr146: } goto st135; tr152: -#line 313 "src/admin.rl" +#line 314 "src/admin.rl" {start(out); palloc_stat(out); end(out);} goto st135; tr160: -#line 312 "src/admin.rl" +#line 313 "src/admin.rl" {start(out); show_slab(out); end(out);} goto st135; tr164: -#line 314 "src/admin.rl" +#line 315 "src/admin.rl" {start(out); show_stat(out);end(out);} goto st135; st135: if ( ++p == pe ) goto _test_eof135; case 135: -#line 422 "src/admin.m" +#line 423 "src/admin.m" goto st0; tr14: -#line 319 "src/admin.rl" +#line 320 "src/admin.rl" {slab_validate(); ok(out);} goto st7; tr21: -#line 307 "src/admin.rl" - {return 0;} +#line 308 "src/admin.rl" + {return -1;} goto st7; tr26: -#line 234 "src/admin.rl" +#line 235 "src/admin.rl" { start(out); tbuf_append(out, help, strlen(help)); @@ -437,9 +438,9 @@ tr26: } goto st7; tr37: -#line 293 "src/admin.rl" +#line 294 "src/admin.rl" {strend = p;} -#line 240 "src/admin.rl" +#line 241 "src/admin.rl" { strstart[strend-strstart]='\0'; start(out); @@ -448,7 +449,7 @@ tr37: } goto st7; tr44: -#line 247 "src/admin.rl" +#line 248 "src/admin.rl" { if (reload_cfg(err)) fail(out, err); @@ -457,11 +458,11 @@ tr44: } goto st7; tr68: -#line 317 "src/admin.rl" +#line 318 "src/admin.rl" {coredump(60); ok(out);} goto st7; tr77: -#line 254 "src/admin.rl" +#line 255 "src/admin.rl" { int ret = snapshot(NULL, 0); @@ -476,9 +477,9 @@ tr77: } goto st7; tr99: -#line 303 "src/admin.rl" +#line 304 "src/admin.rl" { state = false; } -#line 267 "src/admin.rl" +#line 268 "src/admin.rl" { strstart[strend-strstart] = '\0'; if (errinj_set_byname(strstart, state)) { @@ -490,9 +491,9 @@ tr99: } goto st7; tr102: -#line 302 "src/admin.rl" +#line 303 "src/admin.rl" { state = true; } -#line 267 "src/admin.rl" +#line 268 "src/admin.rl" { strstart[strend-strstart] = '\0'; if (errinj_set_byname(strstart, state)) { @@ -504,7 +505,7 @@ tr102: } goto st7; tr118: -#line 210 "src/admin.rl" +#line 211 "src/admin.rl" { tarantool_cfg_iterator_t *i; char *key, *value; @@ -524,15 +525,15 @@ tr118: } goto st7; tr132: -#line 310 "src/admin.rl" +#line 311 "src/admin.rl" {start(out); fiber_info(out); end(out);} goto st7; tr138: -#line 309 "src/admin.rl" +#line 310 "src/admin.rl" {start(out); tarantool_info(out); end(out);} goto st7; tr147: -#line 228 "src/admin.rl" +#line 229 "src/admin.rl" { start(out); errinj_info(out); @@ -540,22 +541,22 @@ tr147: } goto st7; tr153: -#line 313 "src/admin.rl" +#line 314 "src/admin.rl" {start(out); palloc_stat(out); end(out);} goto st7; tr161: -#line 312 "src/admin.rl" +#line 313 "src/admin.rl" {start(out); show_slab(out); end(out);} goto st7; tr165: -#line 314 "src/admin.rl" +#line 315 "src/admin.rl" {start(out); show_stat(out);end(out);} goto st7; st7: if ( ++p == pe ) goto _test_eof7; case 7: -#line 559 "src/admin.m" +#line 560 "src/admin.m" if ( (*p) == 10 ) goto st135; goto st0; @@ -708,28 +709,28 @@ case 23: } goto tr33; tr33: -#line 293 "src/admin.rl" +#line 294 "src/admin.rl" {strstart = p;} goto st24; st24: if ( ++p == pe ) goto _test_eof24; case 24: -#line 719 "src/admin.m" +#line 720 "src/admin.m" switch( (*p) ) { case 10: goto tr36; case 13: goto tr37; } goto st24; tr34: -#line 293 "src/admin.rl" +#line 294 "src/admin.rl" {strstart = p;} goto st25; st25: if ( ++p == pe ) goto _test_eof25; case 25: -#line 733 "src/admin.m" +#line 734 "src/admin.m" switch( (*p) ) { case 10: goto tr36; case 13: goto tr37; @@ -1179,28 +1180,28 @@ case 73: goto tr91; goto st0; tr91: -#line 301 "src/admin.rl" +#line 302 "src/admin.rl" { strstart = p; } goto st74; st74: if ( ++p == pe ) goto _test_eof74; case 74: -#line 1190 "src/admin.m" +#line 1191 "src/admin.m" if ( (*p) == 32 ) goto tr92; if ( 33 <= (*p) && (*p) <= 126 ) goto st74; goto st0; tr92: -#line 301 "src/admin.rl" +#line 302 "src/admin.rl" { strend = p; } goto st75; st75: if ( ++p == pe ) goto _test_eof75; case 75: -#line 1204 "src/admin.m" +#line 1205 "src/admin.m" switch( (*p) ) { case 32: goto st75; case 111: goto st76; @@ -1892,10 +1893,10 @@ case 134: _out: {} } -#line 325 "src/admin.rl" +#line 326 "src/admin.rl" - tbuf_ltrim(&fiber->rbuf, (void *)pe - (void *)fiber->rbuf.data); + in->pos = pe; if (p != pe) { start(out); @@ -1911,17 +1912,20 @@ static void admin_handler(va_list ap) { struct coio coio = va_arg(ap, struct coio); + struct iobuf *iobuf = va_arg(ap, struct iobuf *); lua_State *L = lua_newthread(tarantool_L); int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX); @try { for (;;) { - if (admin_dispatch(&coio, L) < 0) + if (admin_dispatch(&coio, iobuf, L) < 0) return; + iobuf_gc(iobuf); fiber_gc(); } } @finally { luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref); coio_close(&coio); + iobuf_destroy(iobuf); } } diff --git a/src/admin.rl b/src/admin.rl index f1deecde6a..08871e189f 100644 --- a/src/admin.rl +++ b/src/admin.rl @@ -189,8 +189,9 @@ show_stat(struct tbuf *buf) } static int -admin_dispatch(struct coio *coio, lua_State *L) +admin_dispatch(struct coio *coio, struct iobuf *iobuf, lua_State *L) { + struct ibuf *in = &iobuf->in; struct tbuf *out = tbuf_alloc(fiber->gc_pool); struct tbuf *err = tbuf_alloc(fiber->gc_pool); int cs; @@ -198,13 +199,13 @@ admin_dispatch(struct coio *coio, lua_State *L) char *strstart, *strend; bool state; - while ((pe = memchr(fiber->rbuf.data, '\n', fiber->rbuf.size)) == NULL) { - if (coio_bread(coio, &fiber->rbuf, 1) <= 0) + while ((pe = memchr(in->pos, '\n', in->end - in->pos)) == NULL) { + if (coio_bread(coio, in, 1) <= 0) return -1; } pe++; - p = fiber->rbuf.data; + p = in->pos; %%{ action show_configuration { @@ -304,7 +305,7 @@ admin_dispatch(struct coio *coio, lua_State *L) state = state_on | state_off; commands = (help %help | - exit %{return 0;} | + exit %{return -1;} | lua " "+ string %lua | show " "+ info %{start(out); tarantool_info(out); end(out);} | show " "+ fiber %{start(out); fiber_info(out); end(out);} | @@ -324,7 +325,7 @@ admin_dispatch(struct coio *coio, lua_State *L) write exec; }%% - tbuf_ltrim(&fiber->rbuf, (void *)pe - (void *)fiber->rbuf.data); + in->pos = pe; if (p != pe) { start(out); @@ -340,17 +341,20 @@ static void admin_handler(va_list ap) { struct coio coio = va_arg(ap, struct coio); + struct iobuf *iobuf = va_arg(ap, struct iobuf *); lua_State *L = lua_newthread(tarantool_L); int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX); @try { for (;;) { - if (admin_dispatch(&coio, L) < 0) + if (admin_dispatch(&coio, iobuf, L) < 0) return; + iobuf_gc(iobuf); fiber_gc(); } } @finally { luaL_unref(tarantool_L, LUA_REGISTRYINDEX, coro_ref); coio_close(&coio); + iobuf_destroy(iobuf); } } diff --git a/src/coio.m b/src/coio.m index 3ed3d79717..ae88460e7a 100644 --- a/src/coio.m +++ b/src/coio.m @@ -32,6 +32,7 @@ #include <stdio.h> #include "fiber.h" +#include "iobuf.h" #include "sio.h" void @@ -222,7 +223,7 @@ coio_write(struct coio *coio, const void *buf, size_t sz) } ssize_t -coio_writev(struct coio *coio, struct iovec *iov, int iovcnt) +coio_writev(struct coio *coio, struct iovec *iov, int iovcnt, size_t size) { ssize_t total = 0; @try { @@ -232,6 +233,11 @@ coio_writev(struct coio *coio, struct iovec *iov, int iovcnt) ssize_t nwr = sio_writev(coio->ev.fd, iov, iovcnt); if (nwr >= 0) { total += nwr; + /* If there was a hint for the total size + * of the vector, use it. + */ + if (size > 0 && size == total) + return total; iov = sio_advance_iov(iov, &iovcnt, nwr); if (iovcnt == 0) break; @@ -260,18 +266,24 @@ coio_service_on_accept(struct evio_service *evio_service, coio_init(&coio, fd); /* Set connection name. */ - char name[SERVICE_NAME_MAXLEN]; - snprintf(name, sizeof(name), + char fiber_name[SERVICE_NAME_MAXLEN]; + char iobuf_name[SERVICE_NAME_MAXLEN]; + snprintf(fiber_name, sizeof(fiber_name), "%s/%s", evio_service->name, sio_strfaddr(addr)); + snprintf(iobuf_name, sizeof(iobuf_name), "%s/%s", "iobuf", sio_strfaddr(addr)); /* Create the worker fiber. */ + struct iobuf *iobuf = NULL; struct fiber *f; @try { - f = fiber_create(name, service->handler); + iobuf = iobuf_create(iobuf_name); + f = fiber_create(fiber_name, service->handler); } @catch (tnt_Exception *e) { say_error("can't create a handler fiber, dropping client connection"); coio_close(&coio); + if (iobuf) + iobuf_destroy(iobuf); @throw; } /* @@ -283,7 +295,7 @@ coio_service_on_accept(struct evio_service *evio_service, * Start the created fiber. It becomes the coio object owner * and will have to close it and free before termination. */ - fiber_call(f, coio, service->handler_param); + fiber_call(f, coio, iobuf, service->handler_param); } void diff --git a/src/coio_buf.m b/src/coio_buf.m index ac0e5d5c73..3030ded3b3 100644 --- a/src/coio_buf.m +++ b/src/coio_buf.m @@ -28,4 +28,3 @@ */ #include "coio_buf.h" -int coio_readahead; diff --git a/src/fiber.m b/src/fiber.m index 6f806f38d4..bbd5f68420 100644 --- a/src/fiber.m +++ b/src/fiber.m @@ -28,35 +28,19 @@ */ #include "fiber.h" #include "config.h" -#include <arpa/inet.h> #include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <unistd.h> -#include <sysexits.h> -#include <third_party/queue.h> #include <assoc.h> -#include <palloc.h> -#include <salloc.h> #include <say.h> #include <tarantool.h> #include TARANTOOL_CONFIG -#include <tarantool_ev.h> #include <tbuf.h> -#include <util.h> #include <stat.h> #include <pickle.h> -#include "coio_buf.h" +#include "iobuf.h" @implementation FiberCancelException @end @@ -68,22 +52,9 @@ __thread struct fiber *fiber = &sched; static __thread struct fiber *call_stack[FIBER_CALL_STACK]; static __thread struct fiber **sp; static __thread uint32_t last_used_fid; -static __thread struct palloc_pool *ex_pool; static __thread struct mh_i32ptr_t *fibers_registry; __thread SLIST_HEAD(, fiber) fibers, zombie_fibers; -struct fiber_cleanup { - void (*handler) (void *data); - void *data; -}; - -struct fiber_server { - int port; - void *data; - void (*handler) (va_list ap); - void (*on_bind) (void *data); -}; - static void update_last_stack_frame(struct fiber *fiber) { @@ -180,7 +151,6 @@ fiber_cancel(struct fiber *f) * at least that we can't get scheduled ourselves * unless asynchronously woken up is somewhat a relief. */ - fiber_testcancel(); /* Check if we're ourselves cancelled. */ } @@ -194,7 +164,6 @@ fiber_is_cancelled() * cancelled, and raise an exception (FiberCancelException) if * that's the case. */ - void fiber_testcancel(void) { @@ -258,7 +227,6 @@ fiber_sleep(ev_tstamp delay) /** Wait for a forked child to complete. * @note: this is a cancellation point (@sa fiber_testcancel()). */ - void wait_for_child(pid_t pid) { @@ -306,59 +274,15 @@ static void fiber_alloc(struct fiber *fiber) { prelease(fiber->gc_pool); - tbuf_init(&fiber->rbuf, fiber->gc_pool); - tbuf_init(&fiber->iov, fiber->gc_pool); - tbuf_init(&fiber->cleanup, fiber->gc_pool); - fiber->iov_cnt = 0; -} - -void -fiber_register_cleanup(fiber_cleanup_handler handler, void *data) -{ - struct fiber_cleanup i; - i.handler = handler; - i.data = data; - tbuf_append(&fiber->cleanup, &i, sizeof(struct fiber_cleanup)); -} - -void -fiber_cleanup(void) -{ - struct fiber_cleanup *cleanup = fiber->cleanup.data; - int i = fiber->cleanup.size / sizeof(struct fiber_cleanup); - - while (i-- > 0) { - cleanup->handler(cleanup->data); - cleanup++; - } - tbuf_reset(&fiber->cleanup); } void fiber_gc(void) { - struct palloc_pool *tmp; - - fiber_cleanup(); - if (palloc_allocated(fiber->gc_pool) < 128 * 1024) return; - tmp = fiber->gc_pool; - fiber->gc_pool = ex_pool; - ex_pool = tmp; - palloc_set_name(fiber->gc_pool, fiber->name); - palloc_set_name(ex_pool, "ex_pool"); - - struct tbuf tmp_rbuf = fiber->rbuf; - tbuf_init(&fiber->rbuf, fiber->gc_pool); - tbuf_append(&fiber->rbuf, tmp_rbuf.data, tmp_rbuf.size); - - assert(fiber->iov_cnt == 0); - tbuf_init(&fiber->iov, fiber->gc_pool); - tbuf_init(&fiber->cleanup, fiber->gc_pool); - - prelease(ex_pool); + prelease(fiber->gc_pool); } @@ -397,7 +321,6 @@ fiber_loop(void *data __attribute__((unused))) say_error("fiber `%s': exception `%s'", fiber->name, object_getClassName(e)); panic("fiber `%s': exiting", fiber->name); } - tbuf_reset(&fiber->rbuf); fiber_zombificate(); fiber_yield(); /* give control back to scheduler */ } @@ -492,28 +415,9 @@ fiber_destroy_all() fiber_destroy(f); } -void -iov_reset() -{ - fiber->iov_cnt = 0; /* discard anything unwritten */ - tbuf_reset(&fiber->iov); -} - /** * @note: this is a cancellation point. */ - -ssize_t -iov_flush(struct coio *coio) -{ - struct iovec *iov = iovec(&fiber->iov); - size_t iov_cnt = fiber->iov_cnt; - - ssize_t nwr = coio_writev(coio, iov, iov_cnt); - iov_reset(); - return nwr; -} - void fiber_info(struct tbuf *out) { @@ -541,8 +445,6 @@ fiber_init(void) SLIST_INIT(&fibers); fibers_registry = mh_i32ptr_init(); - ex_pool = palloc_create_pool("ex_pool"); - memset(&sched, 0, sizeof(sched)); sched.fid = 1; sched.gc_pool = palloc_create_pool(""); @@ -552,7 +454,7 @@ fiber_init(void) fiber = &sched; last_used_fid = 100; - coio_binit(cfg.readahead); + iobuf_init_readahead(cfg.readahead); } void diff --git a/src/iobuf.m b/src/iobuf.m new file mode 100644 index 0000000000..0c414f7c01 --- /dev/null +++ b/src/iobuf.m @@ -0,0 +1,339 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "iobuf.h" +#include "coio_buf.h" +#include "palloc.h" +#include <stdio.h> + +/* {{{ struct ibuf */ + +/** Initialize an input buffer. */ +static void +ibuf_init(struct ibuf *ibuf, struct palloc_pool *pool) +{ + ibuf->pool = pool; + ibuf->capacity = 0; + ibuf->buf = ibuf->pos = ibuf->end = NULL; + /* Don't allocate the buffer yet. */ +} + +/** Forget all cached input. */ +static void +ibuf_reset(struct ibuf *ibuf) +{ + ibuf->pos = ibuf->end = ibuf->buf; +} + +/** + * Ensure the buffer has sufficient capacity + * to store size bytes. + */ +void +ibuf_reserve(struct ibuf *ibuf, size_t size) +{ + if (size <= ibuf_unused(ibuf)) + return; + size_t current_size = ibuf_size(ibuf); + /* + * Check if we have enough space in the + * current buffer. In this case de-fragment it + * by moving existing data to the beginning. + * Otherwise, get a bigger buffer. + */ + if (size + current_size <= ibuf->capacity) { + memmove(ibuf->buf, ibuf->pos, current_size); + } else { + /* Use cfg_readahead as allocation factor. */ + size_t new_capacity = MAX(ibuf->capacity * 2, cfg_readahead); + while (new_capacity < current_size + size) + new_capacity *= 2; + + ibuf->buf = palloc(ibuf->pool, new_capacity); + memcpy(ibuf->buf, ibuf->pos, current_size); + ibuf->capacity = new_capacity; + } + ibuf->pos = ibuf->buf; + ibuf->end = ibuf->pos + current_size; +} + +/* }}} */ + +/* {{{ struct obuf */ + +/** + * Initialize the next slot in iovec array. The buffer + * always has at least one empty slot. + */ +static inline void +obuf_init_pos(struct obuf *buf, size_t pos) +{ + if (pos >= IOBUF_IOV_MAX) { + tnt_raise(LoggedError, :ER_MEMORY_ISSUE, buf->pos, + "obuf_init_pos", "iovec"); + } + buf->iov[pos].iov_base = NULL; + buf->iov[pos].iov_len = 0; + buf->capacity[pos] = 0; +} + +/** Allocate memory for a single iovec buffer. */ +static inline void +obuf_alloc_pos(struct obuf *buf, size_t pos, size_t size) +{ + size_t capacity = pos > 0 ? buf->capacity[pos-1] * 2 : cfg_readahead; + while (capacity < size) { + capacity *=2; + } + + buf->iov[pos].iov_base = palloc(buf->pool, capacity); + buf->capacity[buf->pos] = capacity; + assert(buf->iov[pos].iov_len == 0); +} + +/** Initialize an output buffer instance. Don't allocate memory + * yet -- it may never be needed. + */ +void +obuf_init(struct obuf *buf, struct palloc_pool *pool) +{ + buf->pool = pool; + buf->pos = 0; + buf->size = 0; + obuf_init_pos(buf, buf->pos); +} + +/** Mark an output buffer as empty. */ +static void +obuf_reset(struct obuf *buf) +{ + buf->pos = 0; + buf->size = 0; + for (struct iovec *iov = buf->iov; iov->iov_len != 0; iov++) { + assert(iov < buf->iov + IOBUF_IOV_MAX); + iov->iov_len = 0; + } +} + +/** Add data to the output buffer. Copies the data. */ +void +obuf_dup(struct obuf *buf, void *data, size_t size) +{ + struct iovec *iov = &buf->iov[buf->pos]; + size_t capacity = buf->capacity[buf->pos]; + /** + * @pre buf->pos points at an array of allocated buffers. + * The array ends with a zero-initialized buffer. + */ + if (iov->iov_len + size > capacity) { + /* + * The data doesn't fit into this buffer. + * It could be because the buffer is not + * allocated, is partially or completely full. + * Copy as much as possible into already + * allocated buffers. + */ + while (iov->iov_len < capacity) { + /* + * This buffer is allocated, but can't + * fit all the data. Copy as much data as + * possible. + */ + size_t fill = capacity - iov->iov_len; + memcpy(iov->iov_base + iov->iov_len, data, fill); + + iov->iov_len += fill; + buf->size += fill; + data += fill; + size -= fill; + + if (size == 0) /* Nothing more to do. */ + return; + buf->pos++; + iov = &buf->iov[buf->pos]; + capacity = buf->capacity[buf->pos]; + } + assert(capacity == 0); + /** + * Still some data to copy. We have to get a new + * buffer. Before we allocate a buffer for this + * position, ensure there is an unallocated buffer + * in the next one, since it works as an end marker + * for the loop above. + */ + obuf_init_pos(buf, buf->pos + 1); + obuf_alloc_pos(buf, buf->pos, size); + } + memcpy(iov->iov_base + iov->iov_len, data, size); + iov->iov_len += size; + buf->size += size; + assert(iov->iov_len <= buf->capacity[buf->pos]); +} + +/** Book a few bytes in the output buffer. */ +void * +obuf_book(struct obuf *buf, size_t size) +{ + struct iovec *iov = &buf->iov[buf->pos]; + size_t capacity = buf->capacity[buf->pos]; + if (iov->iov_len + size > capacity) { + if (iov->iov_len > 0) { + /* Move to the next buffer. */ + buf->pos++; + iov = &buf->iov[buf->pos]; + capacity = buf->capacity[buf->pos]; + } + /* Make sure the next buffer can store size. */ + if (capacity == 0) { + obuf_init_pos(buf, buf->pos + 1); + obuf_alloc_pos(buf, buf->pos, size); + } else if (size > capacity) { + /* Simply realloc. */ + obuf_alloc_pos(buf, buf->pos, size); + } + } + void *booking = iov->iov_base + iov->iov_len; + iov->iov_len += size; + buf->size += size; + assert(iov->iov_len <= buf->capacity[buf->pos]); + return booking; +} + +/** Forget about data in the output buffer beyond the savepoint. */ +void +obuf_rollback_to_svp(struct obuf *buf, struct obuf_svp *svp) +{ + bool is_last_pos = buf->pos == svp->pos; + + buf->pos = svp->pos; + buf->iov[buf->pos].iov_len = svp->iov_len; + buf->size = svp->size; + /** + * We need this check to ensure the following + * loop doesn't run away. + */ + if (is_last_pos) + return; + for (struct iovec *iov = buf->iov + buf->pos + 1; iov->iov_len != 0; iov++) { + assert(iov < buf->iov + IOBUF_IOV_MAX); + iov->iov_len = 0; + } +} + +/* struct obuf }}} */ + +/* {{{ struct iobuf */ + +/** + * How big is a buffer which needs to be shrunk before it is put + * back into buffer cache. + */ +static int iobuf_max_pool_size() +{ + return 18 * cfg_readahead; +} + +SLIST_HEAD(iobuf_cache, iobuf) iobuf_cache; + +/** Create an instance of input/output buffer or take one from cache. */ +struct iobuf * +iobuf_create(const char *name) +{ + struct iobuf *iobuf; + if (SLIST_EMPTY(&iobuf_cache)) { + iobuf = palloc(eter_pool, sizeof(struct iobuf)); + struct palloc_pool *pool = palloc_create_pool(""); + /* Note: do not allocate memory upfront. */ + ibuf_init(&iobuf->in, pool); + obuf_init(&iobuf->out, pool); + } else { + iobuf = SLIST_FIRST(&iobuf_cache); + SLIST_REMOVE_HEAD(&iobuf_cache, next); + } + /* When releasing the buffer, we trim it to iobuf_max_pool_size. */ + assert(palloc_allocated(iobuf->in.pool) <= iobuf_max_pool_size()); + palloc_set_name(iobuf->in.pool, name); + return iobuf; +} + +/** Put an instance back to the iobuf_cache. */ +void +iobuf_destroy(struct iobuf *iobuf) +{ + struct palloc_pool *pool = iobuf->in.pool; + if (palloc_allocated(pool) < iobuf_max_pool_size()) { + ibuf_reset(&iobuf->in); + obuf_reset(&iobuf->out); + } else { + prelease(pool); + ibuf_init(&iobuf->in, pool); + obuf_init(&iobuf->out, pool); + } + palloc_set_name(pool, "iobuf_cache"); + SLIST_INSERT_HEAD(&iobuf_cache, iobuf, next); +} + +/** Send all data in the output buffer and garbage collect. */ +ssize_t +iobuf_flush(struct iobuf *iobuf, struct coio *coio) +{ + int iovcnt = iobuf->out.pos; + if (iobuf->out.iov[iovcnt].iov_len > 0) + iovcnt++; + ssize_t total = coio_writev(coio, iobuf->out.iov, iovcnt, + obuf_size(&iobuf->out)); + iobuf_gc(iobuf); + return total; +} + +void +iobuf_gc(struct iobuf *iobuf) +{ + /* + * If we happen to have fully processed the input, + * move the pos to the start of the input buffer. + * + * If there is some residue, move it as well, + * but only in case if we don't have cfg_readahead + * bytes available for the next round: it's more efficient + * to move any residue now, when it's likely to be small, + * rather than when we have read a bunch more data, and only + * then discovered we don't have enough space to read a + * full request. + */ + if (ibuf_size(&iobuf->in) == 0) + ibuf_reset(&iobuf->in); + else + ibuf_reserve(&iobuf->in, cfg_readahead); + /* Cheap to do even if already done. */ + obuf_reset(&iobuf->out); +} + +int cfg_readahead; + +/* struct iobuf }}} */ diff --git a/src/iproto.m b/src/iproto.m index cb6ea44cfd..b892725d86 100644 --- a/src/iproto.m +++ b/src/iproto.m @@ -28,33 +28,37 @@ */ #include "iproto.h" #include "exception.h" - -#include <stdio.h> #include <string.h> #include <errcode.h> -#include <palloc.h> #include <fiber.h> -#include <tbuf.h> #include <say.h> #include "coio_buf.h" +#include "tbuf.h" const uint32_t msg_ping = 0xff00; -static void iproto_reply(iproto_callback callback, struct tbuf *request); +static inline struct iproto_header * +iproto(const void *pos) +{ + return (struct iproto_header *) pos; +} + +static void +iproto_reply(iproto_callback callback, struct obuf *out, void *req); static void iproto_validate_header(struct iproto_header *header); inline static void -iproto_flush(struct coio *coio, ssize_t to_read) +iproto_flush(struct coio *coio, struct iobuf *iobuf, ssize_t to_read) { /* * Flush output and garbage collect before reading * next header. */ if (to_read > 0) { - iov_flush(coio); + iobuf_flush(iobuf, coio); fiber_gc(); } } @@ -63,8 +67,9 @@ void iproto_interact(va_list ap) { struct coio coio = va_arg(ap, struct coio); + struct iobuf *iobuf = va_arg(ap, struct iobuf *); iproto_callback callback = va_arg(ap, iproto_callback); - struct tbuf *in = &fiber->rbuf; + struct ibuf *in = &iobuf->in; ssize_t to_read = sizeof(struct iproto_header); @try { for (;;) { @@ -72,63 +77,66 @@ iproto_interact(va_list ap) break; /* validating iproto package header */ - iproto_validate_header(iproto(in)); + iproto_validate_header(iproto(in->pos)); ssize_t request_len = sizeof(struct iproto_header) - + iproto(in)->len; - to_read = request_len - in->size; + + iproto(in->pos)->len; + to_read = request_len - ibuf_size(in); - iproto_flush(&coio, to_read); + iproto_flush(&coio, iobuf, to_read); if (to_read > 0 && coio_bread(&coio, in, to_read) <= 0) break; - struct tbuf *request = tbuf_split(in, request_len); - iproto_reply(callback, request); + iproto_reply(callback, &iobuf->out, in->pos); + in->pos += request_len; - to_read = sizeof(struct iproto_header) - in->size; - iproto_flush(&coio, to_read); + to_read = sizeof(struct iproto_header) - ibuf_size(in); + iproto_flush(&coio, iobuf, to_read); } } @finally { coio_close(&coio); + iobuf_destroy(iobuf); } } /** Stack a reply to a single request to the fiber's io vector. */ -static void iproto_reply(iproto_callback callback, struct tbuf *request) +static void +iproto_reply(iproto_callback callback, struct obuf *out, void *req) { - struct iproto_header_retcode *reply; + struct iproto_header_retcode reply; - reply = palloc(fiber->gc_pool, sizeof(*reply)); - reply->msg_code = iproto(request)->msg_code; - reply->sync = iproto(request)->sync; + reply.msg_code = iproto(req)->msg_code; + reply.sync = iproto(req)->sync; - if (unlikely(reply->msg_code == msg_ping)) { - reply->len = 0; - iov_add(reply, sizeof(struct iproto_header)); + if (unlikely(reply.msg_code == msg_ping)) { + reply.len = 0; + obuf_dup(out, &reply, sizeof(struct iproto_header)); return; } + reply.len = sizeof(uint32_t); /* ret_code */ + + void *p_reply = obuf_book(out, sizeof(struct iproto_header_retcode)); + struct obuf_svp svp = obuf_create_svp(out); - reply->len = sizeof(uint32_t); /* ret_code */ - iov_add(reply, sizeof(struct iproto_header_retcode)); - size_t saved_iov_cnt = fiber->iov_cnt; /* make request point to iproto data */ - request->size = iproto(request)->len; - request->data = iproto(request)->data; + struct tbuf request = { + .size = iproto(req)->len, .capacity = iproto(req)->len, + .data = iproto(req)->data, .pool = fiber->gc_pool + }; @try { - callback(reply->msg_code, request); - reply->ret_code = 0; + callback(out, reply.msg_code, &request); + reply.ret_code = 0; } @catch (ClientError *e) { - fiber->iov.size -= (fiber->iov_cnt - saved_iov_cnt) * sizeof(struct iovec); - fiber->iov_cnt = saved_iov_cnt; - reply->ret_code = tnt_errcode_val(e->errcode); - iov_dup(e->errmsg, strlen(e->errmsg)+1); + obuf_rollback_to_svp(out, &svp); + reply.ret_code = tnt_errcode_val(e->errcode); + obuf_dup(out, e->errmsg, strlen(e->errmsg)+1); } - for (; saved_iov_cnt < fiber->iov_cnt; saved_iov_cnt++) - reply->len += iovec(&fiber->iov)[saved_iov_cnt].iov_len; + reply.len += obuf_size(out) - svp.size; + memcpy(p_reply, &reply, sizeof(struct iproto_header_retcode)); } static void diff --git a/src/palloc.m b/src/palloc.m index d2063c3b97..9b71ead523 100644 --- a/src/palloc.m +++ b/src/palloc.m @@ -42,7 +42,7 @@ #include <tbuf.h> #include "exception.h" -#define PALLOC_POOL_NAME_MAXLEN 16 +#define PALLOC_POOL_NAME_MAXLEN 30 struct chunk { uint32_t magic; @@ -69,9 +69,9 @@ TAILQ_HEAD(class_tailq_head, chunk_class) classes; struct palloc_pool { struct chunk_list_head chunks; - SLIST_ENTRY(palloc_pool) link; + SLIST_ENTRY(palloc_pool) link; size_t allocated; - const char *name; + char name[PALLOC_POOL_NAME_MAXLEN]; }; SLIST_HEAD(palloc_pool_head, palloc_pool) pools; @@ -464,7 +464,7 @@ palloc_stat(struct tbuf *buf) void palloc_set_name(struct palloc_pool *pool, const char *name) { - pool->name = name; + snprintf(pool->name, sizeof(pool->name), "%s", name); } size_t diff --git a/src/replica.m b/src/replica.m index 21b9c6ac3a..296452736a 100644 --- a/src/replica.m +++ b/src/replica.m @@ -40,21 +40,27 @@ static void remote_apply_row(struct recovery_state *r, struct tbuf *row); -static struct tbuf * -remote_read_row(struct coio *coio) +static struct tbuf +remote_read_row(struct coio *coio, struct iobuf *iobuf) { - ssize_t to_read = sizeof(struct header_v11) - fiber->rbuf.size; + struct ibuf *in = &iobuf->in; + ssize_t to_read = sizeof(struct header_v11) - ibuf_size(in); if (to_read > 0) - coio_breadn(coio, &fiber->rbuf, to_read); + coio_breadn(coio, in, to_read); - ssize_t request_len = header_v11(&fiber->rbuf)->len + sizeof(struct header_v11); - to_read = request_len - fiber->rbuf.size; + ssize_t request_len = ((struct header_v11 *)in->pos)->len + sizeof(struct header_v11); + to_read = request_len - ibuf_size(in); if (to_read > 0) - coio_breadn(coio, &fiber->rbuf, to_read); - - return tbuf_split(&fiber->rbuf, request_len); + coio_breadn(coio, in, to_read); + + struct tbuf row = { + .size = request_len, .capacity = request_len, + .data = in->pos, .pool = fiber->gc_pool + }; + in->pos += request_len; + return row; } static void @@ -83,8 +89,10 @@ pull_from_remote(va_list ap) { struct recovery_state *r = va_arg(ap, struct recovery_state *); struct coio coio; + struct iobuf *iobuf = NULL; bool warning_said = false; const int reconnect_delay = 1; + coio_clear(&coio); for (;;) { @@ -92,22 +100,26 @@ pull_from_remote(va_list ap) @try { fiber_setcancelstate(true); if (! coio_is_connected(&coio)) { + if (iobuf == NULL) + iobuf = iobuf_create(fiber->name); remote_connect(&coio, &r->remote->addr, r->confirmed_lsn + 1, &err); warning_said = false; } err = "can't read row"; - struct tbuf *row = remote_read_row(&coio); + struct tbuf row = remote_read_row(&coio, iobuf); fiber_setcancelstate(false); err = NULL; - r->remote->recovery_lag = ev_now() - header_v11(row)->tm; + r->remote->recovery_lag = ev_now() - header_v11(&row)->tm; r->remote->recovery_last_update_tstamp = ev_now(); - remote_apply_row(r, row); + remote_apply_row(r, &row); + iobuf_gc(iobuf); fiber_gc(); } @catch (FiberCancelException *e) { + iobuf_destroy(iobuf); coio_close(&coio); @throw; } @catch (tnt_Exception *e) { diff --git a/src/tarantool_lua.m b/src/tarantool_lua.m index 73ba2f328e..50b0143692 100644 --- a/src/tarantool_lua.m +++ b/src/tarantool_lua.m @@ -28,6 +28,7 @@ */ #include <tarantool_lua.h> #include <tarantool.h> +#include "tbuf.h" #include "lua.h" #include "lauxlib.h" diff --git a/test/box/admin.result b/test/box/admin.result index fb318d2c34..02d33af295 100644 --- a/test/box/admin.result +++ b/test/box/admin.result @@ -1,3 +1,4 @@ +exit show stat --- statistics: diff --git a/test/box/admin.test b/test/box/admin.test index 986b4cb56f..b1877799ff 100644 --- a/test/box/admin.test +++ b/test/box/admin.test @@ -4,6 +4,7 @@ import sys # clear statistics: server.stop() server.deploy() +exec admin "exit" exec admin "show stat" exec admin "help" exec admin "show configuration" diff --git a/test/lib/admin_connection.py b/test/lib/admin_connection.py index 7dd17528b5..cdbbfecf1d 100644 --- a/test/lib/admin_connection.py +++ b/test/lib/admin_connection.py @@ -42,7 +42,7 @@ class AdminConnection(TarantoolConnection): buf = self.socket.recv(bufsiz) if not buf: break - res = res + buf; + res = res + buf if (res.rfind("\n...\n") >= 0 or res.rfind("\r\n...\r\n") >= 0): break -- GitLab