diff --git a/client/tarantool_checksum/CMakeLists.txt b/client/tarantool_checksum/CMakeLists.txt index e24280ad6376a39326fba1143b34e237c9ed3676..fadc74533c73788082dfd0338d88dd091e12839c 100644 --- a/client/tarantool_checksum/CMakeLists.txt +++ b/client/tarantool_checksum/CMakeLists.txt @@ -16,6 +16,9 @@ list(APPEND util_checksum_sources ${CMAKE_SOURCE_DIR}/cfg/tarantool_box_cfg.c ${CMAKE_SOURCE_DIR}/cfg/prscfg.c) +list(APPEND util_checksum_sources + ${CMAKE_SOURCE_DIR}/third_party/PMurHash.c) + set_source_files_compile_flags( ${util_checksum_sources}) add_executable(${util_checksum} ${util_checksum_sources}) diff --git a/client/tarantool_checksum/tc_generate.c b/client/tarantool_checksum/tc_generate.c index 3ee329393be449b2d7f100677b7a4e9410a91c0b..2683a217922305815fdb299ebd74ba6ab997afaa 100644 --- a/client/tarantool_checksum/tc_generate.c +++ b/client/tarantool_checksum/tc_generate.c @@ -43,7 +43,7 @@ #include <cfg/prscfg.h> #include <cfg/tarantool_box_cfg.h> -#include <third_party/murmur_hash2.c> +#include <third_party/PMurHash.h> #include <third_party/crc32.h> #include "tc_key.h" @@ -74,7 +74,7 @@ search_hash(const struct tc_key *k, struct tc_space *s) break; } case TC_SPACE_KEY_STRING: - h = MurmurHash2(TC_KEY_DATA(k, i), TC_KEY_SIZE(k, i), h); + h = PMurHash32(h, TC_KEY_DATA(k, i), TC_KEY_SIZE(k, i)); break; case TC_SPACE_KEY_UNKNOWN: assert(1); diff --git a/client/tarantool_checksum/tc_verify.c b/client/tarantool_checksum/tc_verify.c index 18321a55a61464e957ad0a1859dc811bd3559c9b..aac9ba84a65c476287bed0ca5d10e64c3f70dab5 100644 --- a/client/tarantool_checksum/tc_verify.c +++ b/client/tarantool_checksum/tc_verify.c @@ -44,7 +44,6 @@ #include <cfg/prscfg.h> #include <cfg/tarantool_box_cfg.h> -#include <third_party/murmur_hash2.c> #include <third_party/crc32.h> #include "tc_key.h" diff --git a/cmake/BuildLibEIO.cmake b/cmake/BuildLibEIO.cmake index 71ec083fe89afd7156b1e8422415538a00028a2a..3d93759effda0998bbcc7e9a5fad75768aa5a6f5 100644 --- a/cmake/BuildLibEIO.cmake +++ b/cmake/BuildLibEIO.cmake @@ -6,6 +6,7 @@ macro(libeio_build) set(eio_compile_flags "${eio_compile_flags} -Wno-unused-value") set(eio_compile_flags "${eio_compile_flags} -Wno-dangling-else") set(eio_compile_flags "${eio_compile_flags} -DENABLE_BUNDLED_LIBEIO=1") + set(eio_compile_flags "${eio_compile_flags} -DEIO_STACKSIZE=0") set(eio_src ${PROJECT_SOURCE_DIR}/third_party/tarantool_eio.c diff --git a/cmake/BuildMisc.cmake b/cmake/BuildMisc.cmake index 8f90c771b67959f7e1802529e5ae4383c45ccd05..e8975a5021977408b31ff9de8445b63e513472c5 100644 --- a/cmake/BuildMisc.cmake +++ b/cmake/BuildMisc.cmake @@ -5,6 +5,7 @@ macro(libmisc_build) ${PROJECT_SOURCE_DIR}/third_party/crc32.c ${PROJECT_SOURCE_DIR}/third_party/proctitle.c ${PROJECT_SOURCE_DIR}/third_party/qsort_arg.c + ${PROJECT_SOURCE_DIR}/third_party/PMurHash.c ) if (NOT HAVE_MEMMEM) diff --git a/doc/www-data.in/download.cmake b/doc/www-data.in/download.cmake index 5fe64d3a38f70e80f6e0a1713a5932497fe18303..7516b678eed77c99a012ae322a54e5e265d97fec 100644 --- a/doc/www-data.in/download.cmake +++ b/doc/www-data.in/download.cmake @@ -115,7 +115,7 @@ that production systems use the builds provided on this site. We maintain an always up-to-date Debian GNU/Linux and Ubuntu package repository at <a -href="http://tarantool.org/dist/debian">http://tarantoo.org/dist/debian</a> and <a +href="http://tarantool.org/dist/debian">http://tarantool.org/dist/debian</a> and <a href="http://tarantool.org/dist/ubuntu">http://tarantool.org/dist/ubuntu</a> respectively. At the moment the repository contains builds for Debian "Sid", "Squeeze", @@ -129,6 +129,20 @@ At the moment the repository contains builds for Debian "Sid", "Squeeze", sudo apt-get update sudo apt-get install tarantool tarantool-client +### CentOS 5-6 GNU/Linux + +CentOS GNU/Linux repository is available at +<a href="http://tarantool.org/dist/centos">http://tarantool.org/dist/centos</a>. + +Add the following section to your yum repository list (/etc/yum.repos.d/tarantool.repo) +to enable it: + + [tarantool] + name=CentOS-$releasever - Tarantool + baseurl=http://tarantool.org/dist/centos/$releasever/os/$basearch/ + enabled=1 + gpgcheck=0 + # Development branch In the same manner as for [the stable @@ -262,7 +276,7 @@ Tarantool/Box иÑпользует Ñтандартную Ñхему нумер Мы поддерживаем ÑобÑтвенные репозитории .deb пакетов, который Ñодержит поÑледние верÑии Ñервера и клиентÑких библиотек Ð´Ð»Ñ *Debian GNU/Linux* и *Ubuntu*: -<a href="http://tarantool.org/dist/debian">http://tarantoo.org/dist/debian</a>, +<a href="http://tarantool.org/dist/debian">http://tarantool.org/dist/debian</a>, <a href="http://tarantool.org/dist/ubuntu">http://tarantool.org/dist/ubuntu</a>. Репозитории Ñодержат пакеты Ð´Ð»Ñ Debian "Sid", "Squeeze", @@ -277,6 +291,18 @@ Tarantool/Box иÑпользует Ñтандартную Ñхему нумер sudo apt-get update sudo apt-get install tarantool tarantool-client +CentOS GNU/Linux репозиторий доÑтупен по адреÑу: +<a href="http://tarantool.org/dist/centos">http://tarantool.org/dist/centos</a>. + +Добавьте Ñледующую Ñекцию в Ваш ÑпиÑок репозиториев +(/etc/yum.repos.d/tarantool.repo) чтобы Ñделать его доÑтупным Ð´Ð»Ñ Yum: + + [tarantool] + name=CentOS-$releasever - Tarantool + baseurl=http://tarantool.org/dist/centos/$releasever/os/$basearch/ + enabled=1 + gpgcheck=0 + ### Ðрхив Ñтарых релизов Ðрхив Ñтарых релизов доÑтупен по адреÑу <a diff --git a/include/assoc.h b/include/assoc.h index 49db6f4b14ecf2084a0ee33875974d00dfe4b194..497e12ecfc436494988f013ea2c175b1253823ad 100644 --- a/include/assoc.h +++ b/include/assoc.h @@ -82,7 +82,7 @@ static inline int lstrcmp(const void *a, const void *b) return bl - al; return memcmp(a, b, al); } -#include <third_party/murmur_hash2.c> +#include <third_party/PMurHash.h> #define mh_name _lstrptr struct mh_lstrptr_node_t { const void *key; @@ -97,7 +97,7 @@ mh_strptr_hash(const mh_node_t *a, mh_hash_arg_t arg) { (void) arg; const void *_k = (a->key); const u32 l = load_varint32(&_k); - return (u32) MurmurHash2(_k, l, 13); + return PMurHash32(13, _k, l); } #define mh_hash(a, arg) mh_strptr_hash(a, arg) #define mh_eq_arg_t void * diff --git a/include/coeio.h b/include/coeio.h index 0eac1b09834e90b36f497a2af299760a03c373f0..4ddd514015805acd56e1374c3a36f66f3cfd4295 100644 --- a/include/coeio.h +++ b/include/coeio.h @@ -47,22 +47,7 @@ * Yield the current fiber until a created task is complete. */ -/** - * A single task' context. - */ -struct coeio_req { - struct eio_req *req; - struct fiber *f; - bool complete; - bool wait; - void *f_data; - void *result; - struct rlist link; -}; - void coeio_init(void); -void coeio_free(void); -struct coeio_req *coeio_custom(void (*f)(eio_req*), void *arg); -void *coeio_wait(struct coeio_req *r); +ssize_t coeio_custom(ssize_t (*f)(va_list ap), ev_tstamp timeout, ...); #endif /* TARANTOOL_COEIO_H_INCLUDED */ diff --git a/include/coio.h b/include/coio.h index 5b5bb95cb6dba591082066c042d5f4e9f04bee59..da9814f2fed72faa8527cb3468538d4c53933ff5 100644 --- a/include/coio.h +++ b/include/coio.h @@ -29,11 +29,11 @@ * SUCH DAMAGE. */ #include "evio.h" -/* + +/** * Co-operative I/O * Yield the current fiber until IO is ready. */ - struct coio_service { struct evio_service evio_service; @@ -46,11 +46,39 @@ struct coio_service void coio_connect(struct ev_io *coio, struct sockaddr_in *addr); +bool +coio_connect_timeout(struct ev_io *coio, struct sockaddr_in *addr, + socklen_t len, ev_tstamp timeout); + +bool +coio_connect_addrinfo(struct ev_io *coio, struct addrinfo *ai, + ev_tstamp timeout); + +void +coio_bind(struct ev_io *coio, struct sockaddr_in *addr, + socklen_t addrlen); + +int +coio_accept(struct ev_io *coio, struct sockaddr_in *addr, socklen_t addrlen, + ev_tstamp timeout); + void -coio_init(struct ev_io *coio, int fd); +coio_init(struct ev_io *coio); ssize_t -coio_read_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz); +coio_read_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz, + ev_tstamp timeout); + +/** + * Reat at least sz bytes, with readahead. + * + * Returns 0 in case of EOF. + */ +static inline ssize_t +coio_read_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz) +{ + return coio_read_ahead_timeout(coio, buf, sz, bufsiz, TIMEOUT_INFINITY); +} ssize_t coio_readn_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz); @@ -61,18 +89,45 @@ coio_read(struct ev_io *coio, void *buf, size_t sz) return coio_read_ahead(coio, buf, sz, sz); } +static inline ssize_t +coio_read_timeout(struct ev_io *coio, void *buf, size_t sz, ev_tstamp timeout) +{ + return coio_read_ahead_timeout(coio, buf, sz, sz, timeout); +} + static inline ssize_t coio_readn(struct ev_io *coio, void *buf, size_t sz) { return coio_readn_ahead(coio, buf, sz, sz); } -void -coio_write(struct ev_io *coio, const void *buf, size_t sz); +ssize_t +coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz, + ev_tstamp timeout); + +ssize_t +coio_write_timeout(struct ev_io *coio, const void *buf, size_t sz, + ev_tstamp timeout); + +static inline void +coio_write(struct ev_io *coio, const void *buf, size_t sz) +{ + coio_write_timeout(coio, buf, sz, TIMEOUT_INFINITY); +} ssize_t coio_writev(struct ev_io *coio, struct iovec *iov, int iovcnt, size_t size); +ssize_t +coio_sendto_timeout(struct ev_io *coio, const void *buf, size_t sz, int flags, + const struct sockaddr_in *dest_addr, socklen_t addrlen, + ev_tstamp timeout); + +ssize_t +coio_recvfrom_timeout(struct ev_io *coio, void *buf, size_t sz, int flags, + struct sockaddr_in *src_addr, socklen_t addrlen, + ev_tstamp timeout); + void coio_service_init(struct coio_service *service, const char *name, const char *host, int port, diff --git a/include/coio_buf.h b/include/coio_buf.h index 50a025ba88c0550e855af3335f65b53972383586..8adecf53b93eedc8ff0bfc3ebc08d368e33756e5 100644 --- a/include/coio_buf.h +++ b/include/coio_buf.h @@ -32,7 +32,11 @@ #include "iobuf.h" /** Buffered cooperative IO */ -/** Read at least sz bytes, buffered. Return 0 in case of EOF. */ +/** + * Read at least sz bytes, buffered. + * Return the number of bytes read (can be less than n in case + * of EOF). + */ static inline ssize_t coio_bread(struct ev_io *coio, struct ibuf *buf, size_t sz) { @@ -42,6 +46,22 @@ coio_bread(struct ev_io *coio, struct ibuf *buf, size_t sz) return n; } +/** + * Read at least sz bytes buffered or until a timeout reached. + * Return the amount of bytes read (can be less than sz + * in case of EOF or timeout). + */ +static inline ssize_t +coio_bread_timeout(struct ev_io *coio, struct ibuf *buf, size_t sz, + ev_tstamp timeout) +{ + ibuf_reserve(buf, sz); + ssize_t n = coio_read_ahead_timeout(coio, buf->end, sz, ibuf_unused(buf), + timeout); + 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 ev_io *coio, struct ibuf *buf, size_t sz) @@ -52,4 +72,20 @@ coio_breadn(struct ev_io *coio, struct ibuf *buf, size_t sz) return n; } +/** Reat at least sz bytes, buffered. Throw an exception in case + * of EOF. + * @return the number of bytes read. Can be less than sz in + * case of timeout. + */ +static inline ssize_t +coio_breadn_timeout(struct ev_io *coio, struct ibuf *buf, size_t sz, + ev_tstamp timeout) +{ + ibuf_reserve(buf, sz); + ssize_t n = coio_readn_ahead_timeout(coio, buf->end, sz, ibuf_unused(buf), + timeout); + buf->end += n; + return n; +} + #endif /* TARANTOOL_COIO_BUF_H_INCLUDED */ diff --git a/include/evio.h b/include/evio.h index 2e19f7d2e1093e91af3aec0313ae4f8caf8c8405..9994b06c5bc4696d177a2d61b58aadda23f8caf3 100644 --- a/include/evio.h +++ b/include/evio.h @@ -115,16 +115,38 @@ void evio_service_stop(struct evio_service *service); void -evio_clear(struct ev_io *ev); +evio_socket(struct ev_io *coio, int domain, int type, int protocol); void -evio_close(struct ev_io *ev); +evio_close(struct ev_io *evio); static inline bool -evio_is_connected(struct ev_io *ev) +evio_is_active(struct ev_io *ev) { return ev->fd >= 0; } +static inline void +evio_timeout_init(ev_tstamp *start, ev_tstamp *delay, ev_tstamp timeout) +{ + *start = ev_now(); + *delay = timeout; +} + +static inline void +evio_timeout_update(ev_tstamp start, ev_tstamp *delay) +{ + ev_tstamp elapsed = ev_now() - start; + *delay = (elapsed >= *delay) ? 0 : *delay - elapsed; +} + +void +evio_setsockopt_tcp(int fd); + +void +evio_setsockopt_tcpserver(int fd); + +void +evio_bind_addrinfo(struct ev_io *coio, struct addrinfo *ai); #endif /* TARANTOOL_EVIO_H_INCLUDED */ diff --git a/include/ipc.h b/include/ipc.h index a2eccedb0fd10b826f53ab3a1a7c4664e80863f0..a2193177cf4f140a59c5a5978a8c946a123891f0 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -175,6 +175,4 @@ ipc_channel_has_readers(struct ipc_channel *ch); bool ipc_channel_has_writers(struct ipc_channel *ch); -extern const ev_tstamp IPC_TIMEOUT_INFINITY; - #endif /* TARANTOOL_IPC_H_INCLUDED */ diff --git a/include/palloc.h b/include/palloc.h index 2f8935b043a4610d220bca4ec7781a75cc97845a..d945aa5552116b4f81de27ee1e261a895667a8bb 100644 --- a/include/palloc.h +++ b/include/palloc.h @@ -32,6 +32,8 @@ #include <stdint.h> #include "util.h" +#define PALLOC_POOL_NAME_MAXLEN 30 + struct tbuf; struct palloc_pool; diff --git a/include/sio.h b/include/sio.h index 7b3909bce2b3773d4ba87cb0d4b4bdde52494b58..1b9536a6da3d1d8c9f2f1a0a4c804fd0d3e40396 100644 --- a/include/sio.h +++ b/include/sio.h @@ -36,16 +36,20 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netdb.h> #include <fcntl.h> #include "exception.h" enum { SERVICE_NAME_MAXLEN = 32 }; @interface SocketError: SystemError +- (id) init: (int) fd in: (const char *) format: (va_list) ap; - (id) init: (int) fd in: (const char *) format, ...; @end -int sio_socket(void); +int sio_socket(int domain, int type, int protocol); + +int sio_shutdown(int fd, int how); int sio_getfl(int fd); int sio_setfl(int fd, int flag, int on); @@ -60,12 +64,23 @@ sio_getsockopt(int fd, int level, int optname, int sio_connect(int fd, struct sockaddr_in *addr, socklen_t addrlen); int sio_bind(int fd, struct sockaddr_in *addr, socklen_t addrlen); int sio_listen(int fd); +int sio_listen_backlog(); int sio_accept(int fd, struct sockaddr_in *addr, socklen_t *addrlen); ssize_t sio_read(int fd, void *buf, size_t count); +ssize_t sio_read_total(int fd, void *buf, size_t count, size_t total); + ssize_t sio_write(int fd, const void *buf, size_t count); ssize_t sio_writev(int fd, const struct iovec *iov, int iovcnt); +ssize_t sio_write_total(int fd, const void *buf, size_t count, size_t total); + +ssize_t sio_sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr_in *dest_addr, socklen_t addrlen); + +ssize_t sio_recvfrom(int fd, void *buf, size_t len, int flags, + struct sockaddr_in *src_addr, socklen_t *addrlen); + int sio_getpeername(int fd, struct sockaddr_in *addr); const char *sio_strfaddr(struct sockaddr_in *addr); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d4a02ce73c1eada33d14ed67336b59b7ad8bb74..6dbba7e4953e31a0ab8f4e86ca6a02da5b6957f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,7 @@ set (common_sources lua/slab.m lua/uuid.m lua/lua_ipc.m + lua/lua_socket.m lua/session.m ) diff --git a/src/asio.m b/src/asio.m deleted file mode 100644 index f06d9ed85acb2ccec3b3b3a1a5f24621ff7ccce9..0000000000000000000000000000000000000000 --- a/src/asio.m +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 "asio.h" -#include "fiber.h" -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <rlist.h> - -/* - * Asynchronous IO Tasks (libeio wrapper). - * --- - * - * Libeio request processing is designed in edge-trigger - * manner, when libeio is ready to process some requests it - * calls asio_poller callback. - * - * Due to libeio design, asio_poller is called while locks - * are being held, so it's unable to call any libeio function - * inside this callback. - * - * asio_poller triggers asio_watcher to start the polling process. - * In case if none of the requests are complete by that time, it - * starts idle_watcher, which would periodically invoke eio_poll - * until any of requests are complete. - * - * See for details: - * http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod -*/ -struct asio_manager { - ev_idle asio_repeat_watcher; - ev_async asio_watcher; - struct rlist active; - struct rlist complete; - struct rlist ready; -}; - -static struct asio_manager asio_manager; - -inline static void -asio_put(struct asio *a, struct rlist *list) -{ - rlist_del_entry(a, link); - rlist_add_tail_entry(list, a, link); -} - -static void -asio_schedule_repeat(struct ev_idle *w, - int events __attribute__((unused))) -{ - if (eio_poll() != -1) - ev_idle_stop(w); -} - -static void -asio_schedule(struct ev_async *w __attribute__((unused)), - int events __attribute__((unused))) -{ - if (eio_poll() == -1) - ev_idle_start(&asio_manager.asio_repeat_watcher); -} - -static void asio_poller(void) -{ - ev_async_send(&asio_manager.asio_watcher); -} - -/** - * Init asio subsystem. - * - * Create idle and async watchers, init eio. - */ -void -asio_init(void) -{ - memset(&asio_manager, 0, sizeof(struct asio_manager)); - - rlist_init(&asio_manager.active); - rlist_init(&asio_manager.complete); - rlist_init(&asio_manager.ready); - - ev_idle_init(&asio_manager.asio_repeat_watcher, asio_schedule_repeat); - ev_async_init(&asio_manager.asio_watcher, asio_schedule); - ev_async_start(&asio_manager.asio_watcher); - - eio_init(asio_poller, NULL); -} - -static inline void -asio_free_list(struct rlist *list) -{ - struct asio *a; - struct asio *a_next; - a = rlist_first_entry(list, struct asio, link); - while (a != rlist_last_entry(list, struct asio, link)) { - a_next = rlist_next_entry(a, link); - free(a); - a = a_next; - } -} - -/** - * Cancel active tasks and free memory. - */ -void -asio_free(void) -{ - struct asio *a; - /* cancel active requests */ - rlist_foreach_entry(a, &asio_manager.active, link) - asio_cancel(a); - /* free all complete and ready requests */ - asio_free_list(&asio_manager.complete); - asio_free_list(&asio_manager.ready); -} - -inline static struct asio* -asio_alloc(void) -{ - struct asio *a = malloc(sizeof(struct asio)); - if (a == NULL) - return NULL; - memset(a, 0, sizeof(struct asio)); - rlist_init(&a->link); - return a; -} - -static int -asio_on_complete(eio_req *req) -{ - struct asio *a = req->data; - struct fiber *f = a->f; - a->complete = true; - asio_put(a, &asio_manager.complete); - if (a->wakeup) - fiber_wakeup(f); - return 0; -} - -/** - * Create new eio task with specified libeio function and - * argument. - * - * @return asio object pointer, or NULL on error. - * - * @code - * static void request(eio_req *req) { - * (void)req->data; // "arg" - * - * req->result = "result"; - * } - * - * struct asio *a = asio_create(request, "arg"); - * assert(a != NULL); - * - */ -struct asio* -asio_create(void (*f)(eio_req*), void *arg) -{ - struct asio *a; - if (!rlist_empty(&asio_manager.complete)) { - a = rlist_first_entry(&asio_manager.complete, struct asio, link); - } else { - a = asio_alloc(); - if (a == NULL) - return NULL; - } - a->wakeup = false; - a->complete = false; - a->f = fiber; - a->f_data = arg; - a->result = NULL; - a->req = eio_custom(f, 0, asio_on_complete, a); - if (a->req == NULL) - return NULL; - asio_put(a, &asio_manager.active); - return a; -} - -/** - * Yield and wait for a request completion. - * - * @return true if timeout exceeded - */ -bool -asio_wait(struct asio *a, ev_tstamp timeout) -{ - if (a->complete) - return 0; - a->wakeup = true; - bool rc = fiber_yield_timeout(timeout); - fiber_testcancel(); - return rc; -} - -/** - * Cancel eio request. - */ -void -asio_cancel(struct asio *a) -{ - eio_cancel(a->req); - asio_put(a, &asio_manager.ready); -} - -/** - * Finish with request interaction. - */ -void -asio_finish(struct asio *a) -{ - asio_put(a, &asio_manager.ready); -} diff --git a/src/coeio.m b/src/coeio.m index 83bbed9bfffd7bc9c5503fbd50bbdae30af0e8ec..0e200fc48ee398e72ea0cf915127ae20e1757a05 100644 --- a/src/coeio.m +++ b/src/coeio.m @@ -37,51 +37,57 @@ /* * Asynchronous IO Tasks (libeio wrapper). - * --- + * --------------------------------------- * - * Libeio request processing is designed in edge-trigger + * libeio request processing is designed in edge-trigger * manner, when libeio is ready to process some requests it * calls coeio_poller callback. * - * Due to libeio design, coeio_poller is called while locks - * are being held, so it's unable to call any libeio function - * inside this callback. + * Due to libeio design, want_pall callback is called while + * locks are being held, so it's not possible to call any libeio + * function inside this callback. Thus coeio_want_poll raises an + * async event which will be dealt with normally as part of the + * main Tarantool/Box event loop. * - * coeio_poller triggers coeio_watcher to start the polling process. - * In case if none of the requests are complete by that time, it - * starts idle_watcher, which would periodically invoke eio_poll - * until any of requests are complete. + * The async event handler, in turn, performs eio_poll(), which + * will run on_complete callback for all ready eio tasks. + * In case if some of the requests are not complete by the time + * eio_poll() has been called, coeio_idle watcher is started, which + * would periodically invoke eio_poll() until all requests are + * complete. * * See for details: * http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod */ -struct coeio_manager { - ev_idle coeio_repeat_watcher; - ev_async coeio_watcher; - struct rlist active; -}; -static struct coeio_manager coeio_manager; +struct coeio_manager { + ev_idle coeio_idle; + ev_async coeio_async; +} coeio_manager; static void -coeio_schedule_repeat(struct ev_idle *w, - int events __attribute__((unused))) +coeio_idle_cb(struct ev_idle *w, int events __attribute__((unused))) { - if (eio_poll() != -1) + if (eio_poll() != -1) { + /* nothing to do */ ev_idle_stop(w); + } } static void -coeio_schedule(struct ev_async *w __attribute__((unused)), - int events __attribute__((unused))) +coeio_async_cb(struct ev_async *w __attribute__((unused)), + int events __attribute__((unused))) { - if (eio_poll() == -1) - ev_idle_start(&coeio_manager.coeio_repeat_watcher); + if (eio_poll() == -1) { + /* not all tasks are complete. */ + ev_idle_start(&coeio_manager.coeio_idle); + } } -static void coeio_poller(void) +static void +coeio_want_poll_cb(void) { - ev_async_send(&coeio_manager.coeio_watcher); + ev_async_send(&coeio_manager.coeio_async); } /** @@ -92,127 +98,110 @@ static void coeio_poller(void) void coeio_init(void) { - memset(&coeio_manager, 0, sizeof(struct coeio_manager)); + eio_init(coeio_want_poll_cb, NULL); - rlist_init(&coeio_manager.active); + ev_idle_init(&coeio_manager.coeio_idle, coeio_idle_cb); + ev_async_init(&coeio_manager.coeio_async, coeio_async_cb); - ev_idle_init(&coeio_manager.coeio_repeat_watcher, coeio_schedule_repeat); - ev_async_init(&coeio_manager.coeio_watcher, coeio_schedule); - ev_async_start(&coeio_manager.coeio_watcher); - - eio_init(coeio_poller, NULL); + ev_async_start(&coeio_manager.coeio_async); } /** - * Cancel active tasks and free memory. + * A single task context. */ -void -coeio_free(void) -{ - struct coeio_req *r; - struct coeio_req *r_next; - - /* cancel active requests */ - r = rlist_first_entry(&coeio_manager.active, struct coeio_req, link); - while (1) { - if (r == rlist_last_entry(&coeio_manager.active, - struct coeio_req, link)) - break; - r_next = rlist_next_entry(r, link); - /* eio_cancel sets task as cancelled, this guarantees - * that coeio_on_complete would never be called for - * this request, thus we are allowed to free memory here. */ - eio_cancel(r->req); - free(r); - r = r_next; - } -} +struct coeio_task { + /** The calling fiber. */ + struct fiber *fiber; + /** The callback. */ + ssize_t (*func)(va_list ap); + /** + * If the callback sets errno, it's preserved across the + * call. + */ + /** Callback arguments. */ + va_list ap; + /** Callback results. */ + ssize_t result; + int errorno; +}; -inline static struct coeio_req* -coeio_alloc(void) +static void +coeio_custom_cb(eio_req *req) { - struct coeio_req *r = calloc(1, sizeof(struct coeio_req)); - if (r == NULL) { - tnt_raise(LoggedError, :ER_MEMORY_ISSUE, - sizeof(struct coeio_req), "coeio_alloc", - "coeio_req"); - } - rlist_init(&r->link); - return r; + struct coeio_task *task = req->data; + req->result = task->func(task->ap); } +/** + * A callback invoked by eio_poll when associated + * eio_request is complete. + */ static int coeio_on_complete(eio_req *req) { - struct coeio_req *r = req->data; - struct fiber *f = r->f; - r->complete = true; - rlist_del_entry(r, link); - if (r->wait) - fiber_wakeup(f); + /* + * Don't touch the task if the request is cancelled: + * the task is allocated on the caller's stack and + * may be already gone. Don't wakeup the caller + * if the task is cancelled: in this case the caller + * is already woken up, avoid double wake-up. + */ + if (! EIO_CANCELLED(req)) { + struct coeio_task *task = req->data; + task->result = req->result; + task->errorno = req->errorno; + fiber_wakeup(task->fiber); + } return 0; } /** - * Create new eio task with specified libeio function and - * argument. + * Create new eio task with specified function and + * arguments. Yield and wait until the task is complete + * or a timeout occurs. * - * @throws ER_MEMORY_ISSUE + * This function doesn't throw exceptions to avoid double error + * checking: in most cases it's also necessary to check the return + * value of the called function and perform necessary actions. If + * func sets errno, the errno is preserved across the call. * - * @return coeio object pointer. + * @retval -1 and errno = ENOMEM if failed to create a task + * @retval -1 and errno = ETIMEDOUT if timed out + * @retval the function return (errno is preserved). * * @code - * static void request(eio_req *req) { - * (void)req->data; // "arg" - * - * req->result = "result"; + * static ssize_t openfile_cb(va_list ap) + * { + * const char *filename = va_arg(ap); + * int flags = va_arg(ap); + * return open(filename, flags); * } * - * struct coeio_req *r = coeio_custom(request, "arg"); - * - */ -struct coeio_req* -coeio_custom(void (*f)(eio_req*), void *arg) -{ - struct coeio_req *r = coeio_alloc(); - r->f = fiber; - r->f_data = arg; - r->req = eio_custom(f, 0, coeio_on_complete, r); - if (r->req == NULL) { - tnt_raise(LoggedError, :ER_MEMORY_ISSUE, - sizeof(struct eio_req), "coeio_custom", - "eio_req"); - } - rlist_add_tail_entry(&coeio_manager.active, r, link); - return r; -} - -/** - * Yield and wait for a request completion. - * - * @throws FiberCancelException - * - * @return request result pointer. - * - * @code - * struct coeio_req *r = coeio_custom(callback, NULL); - * - * // wait for result and free request object - * void *result = coeio_wait(r); - * - * // continue with result + * if (coeio_custom(openfile_cb, 0.10, "/tmp/file", 0) == -1) + * // handle errors. + * ... */ -void *coeio_wait(struct coeio_req *r) +ssize_t +coeio_custom(ssize_t (*func)(va_list ap), ev_tstamp timeout, ...) { - if (r->complete) { - void *result = r->result; - free(r); - return result; + struct coeio_task task; + task.fiber = fiber; + task.func = func; + task.result = -1; + va_start(task.ap, timeout); + struct eio_req *req = eio_custom(coeio_custom_cb, 0, + coeio_on_complete, &task); + if (req == NULL) { + errno = ENOMEM; + } else if (fiber_yield_timeout(timeout)) { + /* timeout. */ + errno = ETIMEDOUT; + task.result = -1; + eio_cancel(req); + } else { + /* the task is complete. */ + errno = task.errorno; } - r->wait = true; - fiber_yield(); - void *result = r->result; - free(r); - fiber_testcancel(); - return result; + va_end(task.ap); + return task.result; } diff --git a/src/coio.m b/src/coio.m index c1c8a2c746f40edf95c0f503f28d73bc4b0a0a27..d24c67d074f961ee19830cb96bb5952005f41d94 100644 --- a/src/coio.m +++ b/src/coio.m @@ -35,84 +35,159 @@ #include "iobuf.h" #include "sio.h" + /** Note: this function does not throw */ void -coio_init(struct ev_io *coio, int fd) +coio_init(struct ev_io *coio) { - assert(fd >= 0); - /* Prepare for ev events. */ coio->data = fiber; ev_init(coio, (void *) fiber_schedule); - coio->fd = fd; + coio->fd = -1; } /** - * Connect to a host and initialize coio with connected - * socket. + * Connect to a host. */ void coio_connect(struct ev_io *coio, struct sockaddr_in *addr) { - int fd = sio_socket(); - @try { - coio_init(coio, fd); + coio_connect_timeout(coio, addr, sizeof(*addr), + TIMEOUT_INFINITY); +} - int on = 1; - /* libev is non-blocking */ - sio_setfl(fd, O_NONBLOCK, on); +/** + * Connect to a host with a specified timeout. + * @retval true timeout + * @retval false connected + */ +bool +coio_connect_timeout(struct ev_io *coio, struct sockaddr_in *addr, + socklen_t len, ev_tstamp timeout) +{ + if (sio_connect(coio->fd, addr, len) == 0) + return false; + assert(errno == EINPROGRESS); + /* + * Wait until socket is ready for writing or + * timed out. + */ + ev_io_set(coio, coio->fd, EV_WRITE); + ev_io_start(coio); + bool is_timedout = fiber_yield_timeout(timeout); + ev_io_stop(coio); + fiber_testcancel(); + if (is_timedout) { + errno = ETIMEDOUT; + return true; + } + int error = EINPROGRESS; + socklen_t sz = sizeof(error); + sio_getsockopt(coio->fd, SOL_SOCKET, SO_ERROR, + &error, &sz); + if (error != 0) { + errno = error; + tnt_raise(SocketError, :coio->fd in:"connect"); + } + return false; +} - /* - * SO_KEEPALIVE to ensure connections don't hang - * around for too long when a link goes away - */ - sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - &on, sizeof(on)); - /* - * Lower latency is more important than higher - * bandwidth, and we usually write entire - * request/response in a single syscall. - */ - sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, - &on, sizeof(on)); +/** + * Connect to a first address in addrinfo list and initialize coio + * with connected socket. + * + * If coio is already initialized, socket family, + * type and protocol must match the remote address. + * + * @retval true timeout + * @retval false sucess + */ +bool +coio_connect_addrinfo(struct ev_io *coio, struct addrinfo *ai, + ev_tstamp timeout) +{ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + assert(! evio_is_active(coio)); + bool res = true; + while (ai) { + struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr; + @try { + evio_socket(coio, ai->ai_family, + ai->ai_socktype, + ai->ai_protocol); + res = coio_connect_timeout(coio, addr, ai->ai_addrlen, + delay); + return res; + } @catch (SocketError *e) { + if (ai->ai_next == NULL) + @throw; + ev_now_update(); + evio_timeout_update(start, &delay); + } @finally { + if (res) + evio_close(coio); + } + ai = ai->ai_next; + } + /* unreachable. */ + tnt_raise(SocketError, :coio->fd in: "connect_addrinfo()"); +} - if (sio_connect(fd, addr, sizeof(*addr)) < 0) { - assert(errno == EINPROGRESS); - /* Wait until socket is ready for writing. */ - ev_io_set(coio, fd, EV_WRITE); - ev_io_start(coio); - fiber_yield(); - ev_io_stop(coio); - fiber_testcancel(); +/** + * Wait a client connection on a server socket until + * timedout. + */ +int +coio_accept(struct ev_io *coio, struct sockaddr_in *addr, + socklen_t addrlen, ev_tstamp timeout) +{ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); - int error = EINPROGRESS; - socklen_t sz = sizeof(error); - sio_getsockopt(fd, SOL_SOCKET, SO_ERROR, - &error, &sz); - if (error != 0) { - errno = error; - tnt_raise(SocketError, :fd in:"connect"); - } + while (true) { + /* Assume that there are waiting clients + * available */ + int fd = sio_accept(coio->fd, addr, &addrlen); + if (fd >= 0) { + evio_setsockopt_tcp(fd); + return fd; } - } @catch (tnt_Exception *e) { - evio_close(coio); - @throw; + /* The socket is not ready, yield */ + if (! ev_is_active(coio)) { + ev_io_set(coio, coio->fd, EV_READ); + ev_io_start(coio); + } + /* Yield control to other fibers until the timeout + * is being reached. */ + bool is_timedout = fiber_yield_timeout(delay); + fiber_testcancel(); + if (is_timedout) { + errno = ETIMEDOUT; + tnt_raise(SocketError, :coio->fd in:"accept"); + } + evio_timeout_update(start, &delay); } } /** * Read at least sz bytes from socket with readahead. * - * In case of EOF returns 0. + * In case of EOF returns the amount read until eof (possibly 0), + * and sets errno to 0. * Can read up to bufsiz bytes. * - * Returns the number of bytes read. + * @retval the number of bytes read, sets the errno to ETIMEDOUT or 0. */ ssize_t -coio_read_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz) +coio_read_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, + size_t bufsiz, ev_tstamp timeout) { assert(sz <= bufsiz); + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + ssize_t to_read = (ssize_t) sz; @try { while (true) { @@ -129,15 +204,25 @@ coio_read_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz) buf += nrd; bufsiz -= nrd; } else if (nrd == 0) { - return 0; + errno = 0; + return sz - to_read; } /* The socket is not ready, yield */ if (! ev_is_active(coio)) { ev_io_set(coio, coio->fd, EV_READ); ev_io_start(coio); } - fiber_yield(); + /* + * Yield control to other fibers until the + * timeout is being reached. + */ + bool is_timedout = fiber_yield_timeout(delay); fiber_testcancel(); + if (is_timedout) { + errno = ETIMEDOUT; + return sz - to_read; + } + evio_timeout_update(start, &delay); } } @finally { ev_io_stop(coio); @@ -163,6 +248,25 @@ coio_readn_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz) return nrd; } +/** + * Read at least sz bytes, with readahead and timeout. + * + * Treats EOF as an error, and throws an exception. + * + * @retval the number of bytes read, > 0. + */ +ssize_t +coio_readn_ahead_timeout(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz, + ev_tstamp timeout) +{ + ssize_t nrd = coio_read_ahead_timeout(coio, buf, sz, bufsiz, timeout); + if (nrd < sz && errno == 0) { /* EOF. */ + errno = EPIPE; + tnt_raise(SocketError, :coio->fd in:"unexpected EOF when reading " + "from socket"); + } + return nrd; +} /** Write sz bytes to socket. * @@ -170,22 +274,29 @@ coio_readn_ahead(struct ev_io *coio, void *buf, size_t sz, size_t bufsiz) * the socket is not ready, yields the current * fiber until the socket becomes ready, until * all data is written. + * + * @retval the number of bytes written. Can be less than + * requested only in case of timeout. */ -void -coio_write(struct ev_io *coio, const void *buf, size_t sz) +ssize_t +coio_write_timeout(struct ev_io *coio, const void *buf, size_t sz, + ev_tstamp timeout) { + size_t towrite = sz; + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); @try { while (true) { /* * Sic: write as much data as possible, * assuming the socket is ready. */ - ssize_t nwr = sio_write(coio->fd, buf, sz); + ssize_t nwr = sio_write(coio->fd, buf, towrite); if (nwr > 0) { /* Go past the data just written. */ - if (nwr >= sz) - return; - sz -= nwr; + if (nwr >= towrite) + return sz; + towrite -= nwr; buf += nwr; } if (! ev_is_active(coio)) { @@ -195,6 +306,19 @@ coio_write(struct ev_io *coio, const void *buf, size_t sz) /* Yield control to other fibers. */ fiber_yield(); fiber_testcancel(); + /* + * Yield control to other fibers until the + * timeout is reached or the socket is + * ready. + */ + bool is_timedout = fiber_yield_timeout(delay); + fiber_testcancel(); + + if (is_timedout) { + errno = ETIMEDOUT; + return sz - towrite; + } + evio_timeout_update(start, &delay); } } @finally { ev_io_stop(coio); @@ -219,7 +343,8 @@ coio_flush(int fd, struct iovec *iov, ssize_t offset, int iovcnt) } ssize_t -coio_writev(struct ev_io *coio, struct iovec *iov, int iovcnt, size_t size_hint) +coio_writev(struct ev_io *coio, struct iovec *iov, int iovcnt, + size_t size_hint) { ssize_t total = 0; size_t iov_len = 0; @@ -259,6 +384,100 @@ coio_writev(struct ev_io *coio, struct iovec *iov, int iovcnt, size_t size_hint) return total; } +/** + * Send up to sz bytes to a UDP socket. + * Return the number of bytes sent. + * + * @retval 0, errno = ETIMEDOUT timeout + * @retval n the number of bytes written + */ +ssize_t +coio_sendto_timeout(struct ev_io *coio, const void *buf, size_t sz, int flags, + const struct sockaddr_in *dest_addr, socklen_t addrlen, + ev_tstamp timeout) +{ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + @try { + while (true) { + /* + * Sic: write as much data as possible, + * assuming the socket is ready. + */ + ssize_t nwr = sio_sendto(coio->fd, buf, sz, + flags, dest_addr, addrlen); + if (nwr > 0) + return nwr; + if (! ev_is_active(coio)) { + ev_io_set(coio, coio->fd, EV_WRITE); + ev_io_start(coio); + } + /* + * Yield control to other fibers until + * timeout is reached or the socket is + * ready. + */ + bool is_timedout = fiber_yield_timeout(delay); + fiber_testcancel(); + if (is_timedout) { + errno = ETIMEDOUT; + return 0; + } + evio_timeout_update(start, &delay); + } + } @finally { + ev_io_stop(coio); + } +} + +/** + * Read a datagram up to sz bytes from a socket, with a timeout. + * + * @retval 0, errno = 0 eof + * @retval 0, errno = ETIMEDOUT timeout + * @retvl n number of bytes read + */ +ssize_t +coio_recvfrom_timeout(struct ev_io *coio, void *buf, size_t sz, int flags, + struct sockaddr_in *src_addr, socklen_t addrlen, + ev_tstamp timeout) +{ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + + @try { + while (true) { + /* + * Read as much data as possible, + * assuming the socket is ready. + */ + ssize_t nrd = sio_recvfrom(coio->fd, buf, sz, flags, + src_addr, &addrlen); + if (nrd >= 0) + return nrd; + + if (! ev_is_active(coio)) { + ev_io_set(coio, coio->fd, EV_WRITE); + ev_io_start(coio); + } + /* + * Yield control to other fibers until + * timeout is reached or the socket is + * ready. + */ + bool is_timedout = fiber_yield_timeout(delay); + fiber_testcancel(); + if (is_timedout) { + errno = ETIMEDOUT; + return 0; + } + evio_timeout_update(start, &delay); + } + } @finally { + ev_io_stop(coio); + } +} + void coio_service_on_accept(struct evio_service *evio_service, int fd, struct sockaddr_in *addr) @@ -266,7 +485,8 @@ coio_service_on_accept(struct evio_service *evio_service, struct coio_service *service = evio_service->on_accept_param; struct ev_io coio; - coio_init(&coio, fd); + coio_init(&coio); + coio.fd = fd; /* Set connection name. */ char fiber_name[SERVICE_NAME_MAXLEN]; @@ -311,4 +531,3 @@ coio_service_init(struct coio_service *service, const char *name, service->handler = handler; service->handler_param = handler_param; } - diff --git a/src/evio.m b/src/evio.m index 83a2080e97e0824b04750175663adcc06475b00b..fddf5ec2403933b0ad8ac4a6fc6308fd0f87b6de 100644 --- a/src/evio.m +++ b/src/evio.m @@ -38,25 +38,108 @@ #define BIND_RETRY_DELAY 0.1 +/** Note: this function does not throw. */ void -evio_clear(struct ev_io *ev) +evio_close(struct ev_io *evio) { - ev_init(ev, NULL); - ev->data = NULL; - ev->fd = -1; + /* Stop I/O events. Safe to do even if not started. */ + ev_io_stop(evio); + /* Close the socket. */ + close(evio->fd); + /* Make sure evio_is_active() returns a proper value. */ + evio->fd = -1; } -/** Note: this function does not throw. */ +/** + * Create an endpoint for communication. + * Set socket as non-block and apply protocol specific options. + */ void -evio_close(struct ev_io *ev) +evio_socket(struct ev_io *coio, int domain, int type, int protocol) { - /* Stop I/O events. Safe to do even if not started. */ - ev_io_stop(ev); + assert(coio->fd == -1); + /* Don't leak fd if setsockopt fails. */ + coio->fd = sio_socket(domain, type, protocol); + if (type == SOCK_STREAM) { + evio_setsockopt_tcp(coio->fd); + } else { + sio_setfl(coio->fd, O_NONBLOCK, 1); + } +} - /* Close the socket. */ - close(ev->fd); - /* Make sure evio_is_connected() returns a proper value. */ - ev->fd = -1; + +/** Set common tcp socket client options. */ +void +evio_setsockopt_tcp(int fd) +{ + int on = 1; + /* In case this throws, the socket is not leaked. */ + sio_setfl(fd, O_NONBLOCK, on); + /* SO_KEEPALIVE to ensure connections don't hang + * around for too long when a link goes away. + */ + sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + &on, sizeof(on)); + /* + * Lower latency is more important than higher + * bandwidth, and we usually write entire + * request/response in a single syscall. + */ + sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, + &on, sizeof(on)); +} + +/** Set tcp options for server sockets. */ +void +evio_setsockopt_tcpserver(int fd) +{ + int on = 1; + /* In case this throws, the socket is not leaked. */ + sio_setfl(fd, O_NONBLOCK, on); + /* Allow reuse local adresses. */ + sio_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + &on, sizeof(on)); + sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + &on, sizeof(on)); + + /* Send all buffered messages on socket before take + * control out from close(2) or shutdown(2). */ + struct linger linger = { 0, 0 }; + + sio_setsockopt(fd, SOL_SOCKET, SO_LINGER, + &linger, sizeof(linger)); +} + +/** + * Bind to a first address in addrinfo list and initialize coio + * with bound socket. + */ +void +evio_bind_addrinfo(struct ev_io *evio, struct addrinfo *ai) +{ + assert(! evio_is_active(evio)); + int fd = -1; + while (ai) { + struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr; + @try { + fd = sio_socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + evio_setsockopt_tcpserver(fd); + if (sio_bind(fd, addr, ai->ai_addrlen) == 0) { + evio->fd = fd; + return; /* success. */ + } + assert(errno == EADDRINUSE); + } @catch (SocketError *e) { + if (ai->ai_next == NULL) { + close(fd); + @throw; + } + } + close(fd); + ai = ai->ai_next; + } + tnt_raise(SocketError, :evio->fd in:"evio_bind_addrinfo()"); } static inline int @@ -84,22 +167,8 @@ evio_service_accept_cb(ev_io *watcher, if (fd < 0) /* EAGAIN, EWOULDLOCK, EINTR */ return; - - int on = 1; - /* libev is non-blocking */ - sio_setfl(fd, O_NONBLOCK, on); - /* SO_KEEPALIVE to ensure connections don't hang - * around for too long when a link goes away - */ - sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - &on, sizeof(on)); - /* - * Lower latency is more important than higher - * bandwidth, and we usually write entire - * request/response in a single syscall. - */ - sio_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, - &on, sizeof(on)); + /* set common tcp options */ + evio_setsockopt_tcp(fd); /* * Invoke the callback and pass it the accepted * socket. @@ -123,22 +192,10 @@ static int evio_service_bind_and_listen(struct evio_service *service) { /* Create a socket. */ - int fd = sio_socket(); + int fd = sio_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); @try { - int on = 1; - /* Set appropriate options. */ - sio_setfl(fd, O_NONBLOCK, on); - - sio_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)); - sio_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - &on, sizeof(on)); - - struct linger linger = { 0, 0 }; - - sio_setsockopt(fd, SOL_SOCKET, SO_LINGER, - &linger, sizeof(linger)); + evio_setsockopt_tcpserver(fd); if (sio_bind(fd, &service->addr, sizeof(service->addr)) || sio_listen(fd)) { diff --git a/src/ipc.m b/src/ipc.m index aed588e3528bcec1d00dea3e5c477cbaf2eef7f6..db588c6d3ce20368e1ad081c9a741a26723cefb6 100644 --- a/src/ipc.m +++ b/src/ipc.m @@ -31,8 +31,6 @@ #include <stdlib.h> #include <rlist.h> -const ev_tstamp IPC_TIMEOUT_INFINITY = 365*86400*100.0; - struct ipc_channel { struct rlist readers, writers, bcast; unsigned size; @@ -147,7 +145,7 @@ ipc_channel_get_timeout(struct ipc_channel *ch, ev_tstamp timeout) void * ipc_channel_get(struct ipc_channel *ch) { - return ipc_channel_get_timeout(ch, IPC_TIMEOUT_INFINITY); + return ipc_channel_get_timeout(ch, TIMEOUT_INFINITY); } int @@ -200,7 +198,7 @@ ipc_channel_put_timeout(struct ipc_channel *ch, void *data, void ipc_channel_put(struct ipc_channel *ch, void *data) { - ipc_channel_put_timeout(ch, data, IPC_TIMEOUT_INFINITY); + ipc_channel_put_timeout(ch, data, TIMEOUT_INFINITY); } bool diff --git a/src/iproto.m b/src/iproto.m index 75642eb81ecbbb0b6857790c912be05b2d6255e6..e5aabfd7f7c0bfd2e7c9e1b1a3d9a77fd9f7857c 100644 --- a/src/iproto.m +++ b/src/iproto.m @@ -401,7 +401,7 @@ SLIST_HEAD(, iproto_session) iproto_session_cache = static inline bool iproto_session_is_idle(struct iproto_session *session) { - return !evio_is_connected(&session->input) && + return !evio_is_active(&session->input) && ibuf_size(&session->iobuf[0]->in) == 0 && ibuf_size(&session->iobuf[1]->in) == 0; } @@ -751,14 +751,14 @@ iproto_process_request(struct iproto_request *request) struct iobuf *iobuf = request->iobuf; struct port_iproto port; @try { - if (unlikely(! evio_is_connected(&session->output))) + if (unlikely(! evio_is_active(&session->output))) return; fiber_set_sid(fiber, session->sid); iproto_reply(&port, *session->handler, &iobuf->out, header); - if (unlikely(! evio_is_connected(&session->output))) + if (unlikely(! evio_is_active(&session->output))) return; if (! ev_is_active(&session->output)) ev_feed_event(&session->output, EV_WRITE); diff --git a/src/lua/init.m b/src/lua/init.m index f1ad96ca8136fb75ec541ff4651b46231687dfbc..8eca308a1dbd78759009c02bd4fe48dfc6f0e0ee 100644 --- a/src/lua/init.m +++ b/src/lua/init.m @@ -45,6 +45,7 @@ #include "pickle.h" #include "fiber.h" #include "lua_ipc.h" +#include "lua_socket.h" #include "lua/info.h" #include "lua/slab.h" #include "lua/stat.h" @@ -1367,6 +1368,7 @@ tarantool_lua_init() tarantool_lua_stat_init(L); tarantool_lua_ipc_init(L); tarantool_lua_uuid_init(L); + tarantool_lua_socket_init(L); tarantool_lua_session_init(L); mod_lua_init(L); diff --git a/src/lua/lua_ipc.m b/src/lua/lua_ipc.m index 802fe2fb13d3670e79386004f7cb55337b3c303a..596b3a7d104c7cd1da6e966caf24f32d6f106943 100644 --- a/src/lua/lua_ipc.m +++ b/src/lua/lua_ipc.m @@ -117,7 +117,7 @@ lbox_ipc_channel_put(struct lua_State *L) switch (top) { case 2: - timeout = IPC_TIMEOUT_INFINITY; + timeout = TIMEOUT_INFINITY; break; case 3: if (!lua_isnumber(L, -1)) @@ -175,7 +175,7 @@ lbox_ipc_channel_get(struct lua_State *L) if (timeout < 0) luaL_error(L, "wrong timeout"); } else { - timeout = IPC_TIMEOUT_INFINITY; + timeout = TIMEOUT_INFINITY; } struct ipc_channel *ch = lbox_check_channel(L, 1); diff --git a/include/asio.h b/src/lua/lua_socket.h similarity index 61% rename from include/asio.h rename to src/lua/lua_socket.h index 57f404b7e7838495d6f4b7f81221fb1870d8638a..663d5e3b5091162c73b950e98824cf0c7f914219 100644 --- a/include/asio.h +++ b/src/lua/lua_socket.h @@ -1,5 +1,5 @@ -#ifndef TARANTOOL_ASIO_H_INCLUDED -#define TARANTOOL_ASIO_H_INCLUDED +#ifndef TARANTOOL_LUA_SOCKET_H_INCLUDED +#define TARANTOOL_LUA_SOCKET_H_INCLUDED /* * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following @@ -29,43 +29,7 @@ * SUCH DAMAGE. */ -#include "config.h" +struct lua_State; +void tarantool_lua_socket_init(struct lua_State *L); -#include <stdbool.h> -#include <stdint.h> -#include <stdarg.h> -#include <unistd.h> -#include <tarantool_ev.h> -#include <tarantool_eio.h> -#include <coro.h> -#include <util.h> -#include <rlist.h> - -/** - * Asynchronous IO Tasks (libeio wrapper) - * - * Yield the current fiber until a created task is complete. - */ - - -/** - * A single task' context. - */ -struct asio { - struct eio_req *req; - bool complete; - bool wakeup; - struct fiber *f; - void *f_data; - void *result; - struct rlist link; -}; - -void asio_init(void); -void asio_free(void); -struct asio *asio_create(void (*f)(eio_req*), void *arg); -bool asio_wait(struct asio *a, ev_tstamp timeout); -void asio_cancel(struct asio *a); -void asio_finish(struct asio *a); - -#endif /* TARANTOOL_ASIO_H_INCLUDED */ +#endif /* TARANTOOL_LUA_SOCKET_H_INCLUDED */ diff --git a/src/lua/lua_socket.m b/src/lua/lua_socket.m new file mode 100644 index 0000000000000000000000000000000000000000..b7bdfc82424fa3675cae66bfda57d4e4393013f3 --- /dev/null +++ b/src/lua/lua_socket.m @@ -0,0 +1,920 @@ +/* + * 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 "lua_socket.h" +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "sio.h" +#include "evio.h" +#include "coio.h" +#include "coeio.h" +#include "iobuf.h" +#include "coio_buf.h" + +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#include "fiber.h" +#include "tbuf.h" +#include <lua/init.h> +#include <stdlib.h> + +static const char socketlib_name[] = "box.socket"; + +/** + * gethostbyname(), getaddrinfo() and friends do not use + * errno or errno.h errors for their error returns. + * Here we map all failures of name resolution to a single + * socket error number. + */ +enum bio_error { + ERESOLVE = -1 +}; + +/** last operation status */ +enum bio_status { + BIO_ERROR, + BIO_TIMEOUT, + BIO_EOF, + BIO_LIMIT +}; + +struct bio_socket { + struct ev_io coio; + struct iobuf *iob; + /** SOCK_DGRAM or SOCK_STREAM */ + int socktype; + int error; +}; + +static int +bio_pushsocket(struct lua_State *L, int socktype) +{ + struct bio_socket *s = lua_newuserdata(L, sizeof(struct bio_socket)); + luaL_getmetatable(L, socketlib_name); + lua_setmetatable(L, -2); + coio_init(&s->coio); + s->socktype = socktype; + s->iob = NULL; + s->error = 0; + /* + * Do not create a file descriptor yet. Thanks to ipv6, + * socket family is not known until host name is resolved. + * Socket type is saved in s->socktype. + */ + return 1; +} + +static inline struct bio_socket * +bio_checksocket(struct lua_State *L, int narg) +{ + /* avoiding unnecessary luajit assert */ + if (lua_gettop(L) < narg) + luaL_error(L, "box.socket: incorrect method call"); + return luaL_checkudata(L, narg, socketlib_name); +} + +static inline struct bio_socket * +bio_checkactivesocket(struct lua_State *L, int narg) +{ + struct bio_socket *s = bio_checksocket(L, narg); + if (! evio_is_active(&s->coio)) + luaL_error(L, "box.socket: socket is not initialized"); + return s; +} + +/** + * The last error is saved in socket. It can be pretty harmless, + * for example a timeout. Clear the last error before the next + * call. For now clear any error, even a persistent one: it's not + * clear how being any smarter can benefit the library user. + */ +static inline void +bio_clearerr(struct bio_socket *s) +{ + s->error = false; +} + +static void +bio_initbuf(struct bio_socket *s) +{ + assert(s->iob == NULL); + char name[PALLOC_POOL_NAME_MAXLEN]; + const char *type = s->socktype == SOCK_STREAM ? "tcp" : "udp"; + snprintf(name, sizeof(name), "box.io.%s(%d)", + type, s->coio.fd); + s->iob = iobuf_create(name); +} + +static inline int +bio_pushstatus(struct lua_State *L, enum bio_status s) +{ + static char *status_strs[] = {"error", "timeout", "eof", "limit"}; + lua_pushstring(L, status_strs[s]); + return 1; +} + +static int +bio_pusherrorcode(struct lua_State *L, struct bio_socket *s) +{ + lua_pushinteger(L, s->error); + if (s->error >= 0) + lua_pushstring(L, strerror(s->error)); + else if (s->error == ERESOLVE) + lua_pushstring(L, "Host name resolution failed"); + return 2; +} + +/** Error from accept, connect, bind, etc. */ +static int +bio_pusherror(struct lua_State *L, struct bio_socket *s, int errorno) +{ + s->error = errorno; + bio_pushstatus(L, errorno == ETIMEDOUT ? BIO_TIMEOUT : BIO_ERROR); + return 1 + bio_pusherrorcode(L, s); +} + +static int +bio_pushsockerror(struct lua_State *L, struct bio_socket *s, int errorno) +{ + lua_pushnil(L); /* no socket. */ + return 1 + bio_pusherror(L, s, errorno); +} + +/** Error from send */ +static int +bio_pushsenderror(struct lua_State *L, struct bio_socket *s, size_t sz, int errorno) +{ + lua_pushinteger(L, sz); /* sent zero bytes. */ + return 1 + bio_pusherror(L, s, errorno); +} + +/** Error from recv */ +static int +bio_pushrecverror(struct lua_State *L, struct bio_socket *s, int errorno) +{ + lua_pushstring(L, ""); /* received no data. */ + return 1 + bio_pusherror(L, s, errorno); +} + +static inline int +bio_pusheof(struct lua_State *L, struct bio_socket *s) +{ + struct ibuf *in = &s->iob->in; + lua_pushlstring(L, in->pos, ibuf_size(in)); + in->pos += ibuf_size(in); + bio_pushstatus(L, BIO_EOF); + return 2; +} + +/* + * Resolver function, run in separate thread by + * coeio (libeio). +*/ +static ssize_t +bio_getaddrinfo_cb(va_list ap) +{ + const char *host = va_arg(ap, const char *); + const char *port = va_arg(ap, const char *); + const struct addrinfo *hints = va_arg(ap, const struct addrinfo *); + struct addrinfo **res = va_arg(ap, struct addrinfo **); + if (getaddrinfo(host, port, hints, res)) { + errno = ERESOLVE; + return -1; + } + return 0; +} + +static struct addrinfo * +bio_resolve(int socktype, const char *host, const char *port, + ev_tstamp timeout) +{ + /* Fill hinting information for use by connect(2) or bind(2). */ + struct addrinfo *result = NULL; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = socktype; + hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_PASSIVE; + hints.ai_protocol = 0; + /* do resolving */ + if (coeio_custom(bio_getaddrinfo_cb, timeout, host, port, + &hints, &result)) + return NULL; + return result; +} + +static int +lbox_socket_tostring(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, -1); + lua_pushfstring(L, "%d", s->coio.fd); + return 1; +} + +/** + * box.io.tcp() + * + * Create SOCK_STREAM socket object. + */ +static int +lbox_socket_tcp(struct lua_State *L) +{ + return bio_pushsocket(L, SOCK_STREAM); +} + +/** + * box.io.udp() + * + * Create SOCK_DGRAM socket object. + */ +static int +lbox_socket_udp(struct lua_State *L) +{ + return bio_pushsocket(L, SOCK_DGRAM); +} + +/** + * socket:close() + * + * Close the socket. A closed socket should not be used + * any more. + */ +static int +lbox_socket_close(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, -1); + if (! evio_is_active(&s->coio)) + return 0; + if (s->iob) { + iobuf_destroy(s->iob); + s->iob = NULL; + } + evio_close(&s->coio); + bio_clearerr(s); + return 0; +} + +/** + * socket:shutdown(how) + * + * Shut down part of a full-duplex connection. + * + * @retval self success + * @retval nil, status = "error", eno, estr error + */ +static int +lbox_socket_shutdown(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, -1); + int how = luaL_checkint(L, 2); + bio_clearerr(s); + if (shutdown(s->coio.fd, how)) + return bio_pushsockerror(L, s, errno); + /* case #1: Success */ + lua_settop(L, 1); + return 1; +} + +/** + * socket:error() + * + * @return error code and error description of the last error. + */ +static int +lbox_socket_error(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, -1); + return bio_pusherrorcode(L, s); +} + +/** + * socket:connect(host, port [, timeout]) + * + * Connect socket to a host. + * + * @retval self success + * @retval nil, status = "error", eno, estr error + * @retval nil, status = "timeout", eno, estr timeout + */ +static int +lbox_socket_connect(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, 1); + const char *host = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 4) + timeout = luaL_checknumber(L, 4); + if (evio_is_active(&s->coio)) + return bio_pushsockerror(L, s, EALREADY); + bio_clearerr(s); + + /* try to resolve a hostname */ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + struct addrinfo *ai = bio_resolve(s->socktype, host, port, delay); + if (ai == NULL) + return bio_pushsockerror(L, s, errno); + + evio_timeout_update(start, &delay); + @try { + /* connect to a first available host */ + if (coio_connect_addrinfo(&s->coio, ai, delay)) + return bio_pushsockerror(L, s, ETIMEDOUT); + } @catch (SocketError *e) { + return bio_pushsockerror(L, s, errno); + } @finally { + freeaddrinfo(ai); + } + bio_initbuf(s); + /* Success */ + lua_settop(L, 1); + return 1; +} + +/** + * socket:send(data [, timeout]) + * + * Send data to a socket. + * + * In case of socket an error or timeout send() returns + * the number of bytes written before the error occurred. + * + * + * @retval size success + * @retval size, status = "timeout", eno, estr timeout + * @retval size, status = "error", eno, estr error + * + */ +static int +lbox_socket_send(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + size_t buf_size = 0; + const char *buf = luaL_checklstring(L, 2, &buf_size); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 3) + timeout = luaL_checknumber(L, 3); + if (s->iob == NULL) + return bio_pushsenderror(L, s, 0, ENOTCONN); + bio_clearerr(s); + @try { + ssize_t nwr = coio_write_timeout(&s->coio, buf, buf_size, + timeout); + if (nwr < buf_size) + return bio_pushsenderror(L, s, nwr, ETIMEDOUT); + } @catch (SocketError *e) { + return bio_pushsenderror(L, s, 0, errno); + } + /* case #1: Success */ + lua_pushinteger(L, buf_size); + return 1; +} + +/** + * socket:recv(size [, timeout]) + * + * Try to read size bytes from a socket. + * + * In case of timeout all read data will be available on next + * read. In case of read error the data is thrown away. + * + * @retval data success + * @retval data = "", status = "error", eno, estr socket error + * @retval data = "", status = "timeout", eno, estr read timeout + * @retval data = chunk, status = "eof" eof + */ +static int +lbox_socket_recv(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + int sz = luaL_checkint(L, 2); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) >= 3) + timeout = luaL_checknumber(L, 3); + if (s->iob == NULL) + return bio_pushrecverror(L, s, ENOTCONN); + /* Clear possible old timeout status. */ + bio_clearerr(s); + /* + * Readahead buffer can contain sufficient amount of + * data from the previous call to cover the required read + * size. + * + * If not, try to read as much as possible to readahead + * until it's more or equal to the required size. + */ + struct ibuf *in = &s->iob->in; + ssize_t to_read = sz - ibuf_size(in); + + if (to_read > 0) { + ssize_t nrd; + @try { + nrd = coio_bread_timeout(&s->coio, in, to_read, + timeout); + } @catch (SocketError *e) { + return bio_pushrecverror(L, s, errno); + } + if (nrd < to_read) { + /* timeout or EOF. */ + if (errno == ETIMEDOUT) + return bio_pushrecverror(L, s, ETIMEDOUT); + return bio_pusheof(L, s); + } + } + lua_pushlstring(L, in->pos, sz); + in->pos += sz; + return 1; +} + +struct readline_state { + int pos; + const char *sep; + size_t sep_size; +}; + +static void +readline_state_init(struct lua_State *L, struct readline_state *rs, int idx) +{ + int i = 0; + lua_pushnil(L); + while (lua_next(L, idx) != 0) { + rs[i].pos = 0; + rs[i].sep = luaL_checklstring(L, -1, &rs[i].sep_size); + if (rs[i].sep_size == 0) + luaL_error(L, "box.io.readline: bad separator"); + lua_pop(L, 1); + i++; + } +} + +static inline int +readline_state_try(struct readline_state *rs, int i, char chr) +{ + if (unlikely(rs[i].sep[ rs[i].pos ] == chr)) { + if (unlikely(rs[i].sep_size == rs[i].pos + 1)) + return i; + rs[i].pos++; + } else { + rs[i].pos = 0; + } + return -1; +} + +static int +readline_state_next(struct readline_state *rs, int size, char chr) +{ + int i; + for (i = 0; i < size; i++) { + /* we have to repeat state check to ensure that we + * don't miss new separator state after previous + * reset. */ + if (unlikely(rs[i].sep[ rs[i].pos ] == chr)) { +first_matched: if (unlikely(rs[i].sep_size == rs[i].pos + 1)) + return i; + rs[i].pos++; + continue; + } + rs[i].pos = 0; + if (unlikely(rs[i].sep[ rs[i].pos ] == chr)) + goto first_matched; + } + return -1; +} + +static void +lbox_socket_readline_cr(struct lua_State *L) +{ + /* emulate user passed {'\n'} as the separate table */ + lua_newtable(L); + lua_pushnumber(L, 1); + lua_pushstring(L, "\n"); + lua_rawset(L, -3); +} + +static int +lbox_socket_readline_opts(struct lua_State *L, unsigned int *limit, + double *timeout) +{ + int seplist = 2; + switch (lua_gettop(L)) { + case 1: + /* readline() */ + lbox_socket_readline_cr(L); + break; + case 2: + /* readline(limit) + readline({seplist}) */ + if (lua_isnumber(L, 2)) { + *limit = luaL_checkint(L, 2); + lbox_socket_readline_cr(L); + seplist = 3; + } else if (! lua_istable(L, 2)) + luaL_error(L, "box.io.readline: bad argument"); + break; + case 3: + /* readline(limit, timeout) + * readline(limit, {seplist}) + * readline({seplist}, timeout) */ + if (lua_isnumber(L, 2)) { + *limit = luaL_checkint(L, 2); + if (lua_isnumber(L, 3)) { + *timeout = luaL_checknumber(L, 3); + lbox_socket_readline_cr(L); + seplist = 4; + break; + } else if (! lua_istable(L, 3)) + luaL_error(L, "box.io.readline: bad argument"); + seplist = 3; + break; + } else if (! lua_istable(L, 2)) + luaL_error(L, "box.io.readline: bad argument"); + *timeout = luaL_checknumber(L, 3); + seplist = 2; + break; + case 4: + /* readline(limit, {seplist}, timeout) */ + *limit = luaL_checkint(L, 2); + if (! lua_istable(L, 3)) + luaL_error(L, "box.io.readline: bad argument"); + seplist = 3; + *timeout = luaL_checknumber(L, 4); + break; + default: + luaL_error(L, "box.io.readline: bad argument"); + break; + } + return seplist; +} + +/** + * socket:readline(limit, seplist, timeout) + * + * Possible usage: + * + * readline() == readline(limit == inf, seplist == {'\n'}, timeout == inf) + * readline(limit) + * readline(limit, timeout) + * readline({seplist}) + * readline(limit, {seplist}) + * readline({seplist}, timeout) + * readline(limit, {seplist}, timeout) + * + * In case of socket error and timeout all read data will be + * available on next read. + * + * @retval data, nil, sep = str success + * @retval data = "", status = "timeout", eno, estr timeout + * @retval data = "", status = "error", eno, estr error + * @retval data = chunk, status = "limit" limit + * @retval data = chunk, status = "eof" eof + */ +static int +lbox_socket_readline(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + if (s->iob == NULL) + return bio_pushrecverror(L, s, ENOTCONN); + bio_clearerr(s); + + unsigned int limit = UINT_MAX; + double timeout = TIMEOUT_INFINITY; + int seplist = lbox_socket_readline_opts(L, &limit, &timeout); + + int rs_size = lua_objlen(L, seplist); + if (rs_size == 0) + luaL_error(L, "box.io.readline: bad separator table"); + + size_t bottom = 0; + int match; + struct ibuf *in = &s->iob->in; + + @try { + /* readline implementation uses a simple state machine + * to determine current position of a possible + * separator. */ + struct readline_state *rs = + palloc(in->pool, sizeof(struct readline_state) * rs_size); + readline_state_init(L, rs, seplist); + + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + + while (1) { + + /* case #4: user limit reached */ + if (bottom == limit) { + lua_pushlstring(L, in->pos, bottom); + s->iob->in.pos += bottom; + bio_pushstatus(L, BIO_LIMIT); + return 2; + } + + /* if current read position (bottom) equals to + * the readahead size, then read new data. */ + if (bottom == ibuf_size(in)) { + + ssize_t nrd = coio_bread_timeout(&s->coio, &s->iob->in, 1, + delay); + /* case #5: eof (step 1)*/ + if (nrd == 0) { + if (errno == ETIMEDOUT) + return bio_pushrecverror(L, s, ETIMEDOUT); + return bio_pusheof(L, s); + } + } + + match = readline_state_next(rs, rs_size, in->pos[bottom]); + bottom++; + if (match >= 0) + break; + + evio_timeout_update(start, &delay); + } + } @catch (SocketError *e) { + return bio_pushrecverror(L, s, errno); + } + + /* case #1: success, separator matched */ + lua_pushlstring(L, in->pos, bottom); + in->pos += bottom; + lua_pushnil(L); + lua_pushinteger(L, match + 1); + return 3; +} + +/** + * socket:bind(host, port [, timeout]) + * + * Bind a socket to the given host. + * + * @retval socket success + * @retval nil, status = "error", eno, estr error + * @retval nil, status = "timeout", eno, estr timeout + */ +static int +lbox_socket_bind(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, 1); + const char *host = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 4) + timeout = luaL_checknumber(L, 4); + if (evio_is_active(&s->coio)) + return bio_pusherror(L, s, EALREADY); + bio_clearerr(s); + /* try to resolve a hostname */ + struct addrinfo *ai = bio_resolve(s->socktype, host, port, timeout); + if (ai == NULL) + return bio_pusherror(L, s, errno); + @try { + evio_bind_addrinfo(&s->coio, ai); + } @catch (SocketError *e) { + /* case #2: error */ + return bio_pusherror(L, s, errno); + } @finally { + freeaddrinfo(ai); + } + /* case #1: Success */ + lua_settop(L, 1); + return 1; +} + +/** + * socket:listen() + * + * Marks the socket that it will be used to accept incoming + * connection requests using socket:accept(). + * + * @retval socket (self) on success + * @retval nil, status = "error", errno, error string + */ +static int +lbox_socket_listen(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + bio_clearerr(s); + if (listen(s->coio.fd, sio_listen_backlog())) + return bio_pusherror(L, s, errno); + lua_settop(L, 1); + return 1; +} + +/** + * socket:accept([timeout]) + * + * Wait for a new client connection and create connected + * socket. + * + * @retval socket (client), nil, address, port success + * @retval nil, status = "error", eno, estr error + * @retval nil, status = "timeout", eno, estr timeout + */ +static int +lbox_socket_accept(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 2) + timeout = luaL_checknumber(L, 2); + bio_clearerr(s); + + struct sockaddr_in addr; + /* push client socket */ + bio_pushsocket(L, SOCK_STREAM); + struct bio_socket *client = lua_touserdata(L, -1); + @try { + client->coio.fd = coio_accept(&s->coio, &addr, sizeof(addr), + timeout); + } @catch (SocketError *e) { + return bio_pusherror(L, s, errno); + } + /* get user host and port */ + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int rc = getnameinfo((struct sockaddr*)&addr, sizeof(addr), + hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICHOST|NI_NUMERICSERV); + if (rc != 0) + return bio_pusherror(L, s, ERESOLVE); + + bio_initbuf(client); + lua_pushnil(L); /* status */ + /* push host and port */ + lua_pushstring(L, hbuf); + lua_pushstring(L, sbuf); + return 4; +} + +/** + * socket:sendto(buf, host, port [, timeout]) + * + * Send a message on a UDP socket to a specified host. + * + * @retval size success + * @retval 0, status = "error", eno, estr error + * @retval 0, status = "timeout", eno, estr timeout + */ +static int +lbox_socket_sendto(struct lua_State *L) +{ + struct bio_socket *s = bio_checksocket(L, 1); + size_t buf_size = 0; + const char *buf = luaL_checklstring(L, 2, &buf_size); + const char *host = luaL_checkstring(L, 3); + const char *port = luaL_checkstring(L, 4); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 5) + timeout = luaL_checknumber(L, 5); + bio_clearerr(s); + + /* try to resolve a hostname */ + ev_tstamp start, delay; + evio_timeout_init(&start, &delay, timeout); + struct addrinfo *a = bio_resolve(s->socktype, host, port, delay); + if (a == NULL) + return bio_pushsenderror(L, s, 0, errno); + + evio_timeout_update(start, &delay); + size_t nwr; + @try { + /* maybe init the socket */ + struct sockaddr_in *addr = (struct sockaddr_in *) a->ai_addr; + if (! evio_is_active(&s->coio)) + evio_socket(&s->coio, addr->sin_family, s->socktype, 0); + + nwr = coio_sendto_timeout(&s->coio, buf, buf_size, 0, + addr, a->ai_addrlen, delay); + } @catch (SocketError *e) { + /* case #2-3: error or timeout */ + return bio_pushsenderror(L, s, 0, errno); + } @finally { + freeaddrinfo(a); + } + if (nwr == 0) { + assert(errno == ETIMEDOUT); + return bio_pushsenderror(L, s, 0, ETIMEDOUT); + } + lua_pushinteger(L, nwr); + return 1; +} + +/** + * socket:recvfrom(limit [, timeout]) + * + * Receive a message on a UDP socket. + * + * @retval data, nil, client_addr, client_port success + * @retval data = "", status = "error", eno, estr error + * @retval data = "", status = "timeout", eno, estr timeout + * @retval data, status = "eof" eof + */ +static int +lbox_socket_recvfrom(struct lua_State *L) +{ + struct bio_socket *s = bio_checkactivesocket(L, 1); + int buf_size = luaL_checkint(L, 2); + double timeout = TIMEOUT_INFINITY; + if (lua_gettop(L) == 3) + timeout = luaL_checknumber(L, 3); + bio_clearerr(s); + + /* Maybe initialize the buffer, can throw ER_MEMORY_ISSUE. */ + if (s->iob == NULL) + bio_initbuf(s); + + struct sockaddr_in addr; + struct ibuf *in = &s->iob->in; + size_t nrd; + @try { + ibuf_reserve(in, buf_size); + nrd = coio_recvfrom_timeout(&s->coio, in->pos, buf_size, 0, &addr, + sizeof(addr), timeout); + } @catch (SocketError *e) { + return bio_pushrecverror(L, s, errno); + } + if (nrd == 0) { + if (errno == ETIMEDOUT) + return bio_pushrecverror(L, s, errno); + return bio_pusheof(L, s); + } + + /* push host and port */ + char hbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; + int rc = getnameinfo((struct sockaddr*)&addr, sizeof(addr), + hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICHOST|NI_NUMERICSERV); + if (rc != 0) { + /* case #2: error */ + return bio_pushrecverror(L, s, ERESOLVE); + } + + /* case #1: push received data */ + lua_pushlstring(L, in->pos, nrd); + lua_pushnil(L); + lua_pushstring(L, hbuf); + lua_pushstring(L, sbuf); + return 4; +} + +void +tarantool_lua_socket_init(struct lua_State *L) +{ + static const struct luaL_reg lbox_socket_meta[] = { + {"__gc", lbox_socket_close}, + {"__tostring", lbox_socket_tostring}, + {"error", lbox_socket_error}, + {"close", lbox_socket_close}, + {"shutdown", lbox_socket_shutdown}, + {"connect", lbox_socket_connect}, + {"send", lbox_socket_send}, + {"recv", lbox_socket_recv}, + {"readline", lbox_socket_readline}, + {"bind", lbox_socket_bind}, + {"listen", lbox_socket_listen}, + {"accept", lbox_socket_accept}, + {"sendto", lbox_socket_sendto}, + {"recvfrom", lbox_socket_recvfrom}, + {NULL, NULL} + }; + static const struct luaL_reg socketlib[] = { + {"tcp", lbox_socket_tcp}, + {"udp", lbox_socket_udp}, + {NULL, NULL} + }; + tarantool_lua_register_type(L, socketlib_name, lbox_socket_meta); + luaL_register(L, socketlib_name, socketlib); + lua_pop(L, 1); +} diff --git a/src/memcached-grammar.m b/src/memcached-grammar.m index e0f341415b7fb920eb45bcd7cfef68ea4e81a4b2..b5da180f3ae20fc144276d2cae4adcf7ee7cc160 100644 --- a/src/memcached-grammar.m +++ b/src/memcached-grammar.m @@ -256,14 +256,15 @@ case 12: tr26: #line 247 "src/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -280,7 +281,7 @@ tr26: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -297,14 +298,15 @@ tr26: } goto st197; tr30: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -321,7 +323,7 @@ tr30: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -338,16 +340,17 @@ tr30: } goto st197; tr39: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -364,7 +367,7 @@ tr39: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -383,14 +386,15 @@ tr39: tr58: #line 247 "src/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -407,7 +411,7 @@ tr58: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -442,14 +446,15 @@ tr58: } goto st197; tr62: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -466,7 +471,7 @@ tr62: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -501,16 +506,17 @@ tr62: } goto st197; tr71: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -527,7 +533,7 @@ tr71: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -564,14 +570,15 @@ tr71: tr91: #line 248 "src/memcached-grammar.rl" {cas = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -588,7 +595,7 @@ tr91: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -607,14 +614,15 @@ tr91: } goto st197; tr95: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -631,7 +639,7 @@ tr95: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -650,16 +658,17 @@ tr95: } goto st197; tr105: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -676,7 +685,7 @@ tr105: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -697,9 +706,9 @@ tr105: tr118: #line 249 "src/memcached-grammar.rl" {incr = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -762,9 +771,9 @@ tr118: } goto st197; tr122: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -827,11 +836,11 @@ tr122: } goto st197; tr132: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -894,9 +903,9 @@ tr132: } goto st197; tr141: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -928,9 +937,9 @@ tr146: if (exptime > 0 && exptime <= 60*60*24*30) exptime = exptime + ev_now(); } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -956,11 +965,11 @@ tr146: } goto st197; tr157: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -986,9 +995,9 @@ tr157: } goto st197; tr169: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1004,9 +1013,9 @@ tr169: tr174: #line 250 "src/memcached-grammar.rl" {flush_delay = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1020,11 +1029,11 @@ tr174: } goto st197; tr185: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1038,9 +1047,9 @@ tr185: } goto st197; tr195: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1059,9 +1068,9 @@ tr195: } goto st197; tr213: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1075,14 +1084,15 @@ tr213: tr233: #line 247 "src/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1099,7 +1109,7 @@ tr233: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1116,14 +1126,15 @@ tr233: } goto st197; tr237: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1140,7 +1151,7 @@ tr237: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1157,16 +1168,17 @@ tr237: } goto st197; tr246: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1183,7 +1195,7 @@ tr246: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1202,14 +1214,15 @@ tr246: tr263: #line 247 "src/memcached-grammar.rl" {bytes = natoq(fstart, p);} -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1226,7 +1239,7 @@ tr263: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1239,14 +1252,15 @@ tr263: } goto st197; tr267: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1263,7 +1277,7 @@ tr267: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1276,16 +1290,17 @@ tr267: } goto st197; tr276: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } #line 252 "src/memcached-grammar.rl" { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the @@ -1302,7 +1317,7 @@ tr276: goto exit; } } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1315,9 +1330,9 @@ tr276: } goto st197; tr281: -#line 280 "src/memcached-grammar.rl" +#line 281 "src/memcached-grammar.rl" { p++; } -#line 274 "src/memcached-grammar.rl" +#line 275 "src/memcached-grammar.rl" { done = true; stats.bytes_read += p - in->pos; @@ -1332,21 +1347,21 @@ st197: if ( ++p == pe ) goto _test_eof197; case 197: -#line 1336 "src/memcached-grammar.m" +#line 1351 "src/memcached-grammar.m" goto st0; tr27: #line 247 "src/memcached-grammar.rl" {bytes = natoq(fstart, p);} goto st13; tr40: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st13; st13: if ( ++p == pe ) goto _test_eof13; case 13: -#line 1350 "src/memcached-grammar.m" +#line 1365 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr30; goto st0; @@ -1358,7 +1373,7 @@ st14: if ( ++p == pe ) goto _test_eof14; case 14: -#line 1362 "src/memcached-grammar.m" +#line 1377 "src/memcached-grammar.m" switch( (*p) ) { case 32: goto st14; case 78: goto st15; @@ -1472,18 +1487,18 @@ case 26: goto tr45; goto st0; tr45: -#line 288 "src/memcached-grammar.rl" +#line 289 "src/memcached-grammar.rl" {append = true; } goto st27; tr209: -#line 289 "src/memcached-grammar.rl" +#line 290 "src/memcached-grammar.rl" {append = false;} goto st27; st27: if ( ++p == pe ) goto _test_eof27; case 27: -#line 1487 "src/memcached-grammar.m" +#line 1502 "src/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st27; @@ -1509,7 +1524,7 @@ st28: if ( ++p == pe ) goto _test_eof28; case 28: -#line 1513 "src/memcached-grammar.m" +#line 1528 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st29; goto st0; @@ -1530,7 +1545,7 @@ st30: if ( ++p == pe ) goto _test_eof30; case 30: -#line 1534 "src/memcached-grammar.m" +#line 1549 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr50; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1544,7 +1559,7 @@ st31: if ( ++p == pe ) goto _test_eof31; case 31: -#line 1548 "src/memcached-grammar.m" +#line 1563 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st31; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1558,7 +1573,7 @@ st32: if ( ++p == pe ) goto _test_eof32; case 32: -#line 1562 "src/memcached-grammar.m" +#line 1577 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr54; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1576,7 +1591,7 @@ st33: if ( ++p == pe ) goto _test_eof33; case 33: -#line 1580 "src/memcached-grammar.m" +#line 1595 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st33; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1590,7 +1605,7 @@ st34: if ( ++p == pe ) goto _test_eof34; case 34: -#line 1594 "src/memcached-grammar.m" +#line 1609 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr58; case 13: goto tr59; @@ -1604,14 +1619,14 @@ tr59: {bytes = natoq(fstart, p);} goto st35; tr72: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st35; st35: if ( ++p == pe ) goto _test_eof35; case 35: -#line 1615 "src/memcached-grammar.m" +#line 1630 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr62; goto st0; @@ -1623,7 +1638,7 @@ st36: if ( ++p == pe ) goto _test_eof36; case 36: -#line 1627 "src/memcached-grammar.m" +#line 1642 "src/memcached-grammar.m" switch( (*p) ) { case 32: goto st36; case 78: goto st37; @@ -1747,7 +1762,7 @@ st48: if ( ++p == pe ) goto _test_eof48; case 48: -#line 1751 "src/memcached-grammar.m" +#line 1766 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st49; goto st0; @@ -1768,7 +1783,7 @@ st50: if ( ++p == pe ) goto _test_eof50; case 50: -#line 1772 "src/memcached-grammar.m" +#line 1787 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr79; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1782,7 +1797,7 @@ st51: if ( ++p == pe ) goto _test_eof51; case 51: -#line 1786 "src/memcached-grammar.m" +#line 1801 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st51; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1796,7 +1811,7 @@ st52: if ( ++p == pe ) goto _test_eof52; case 52: -#line 1800 "src/memcached-grammar.m" +#line 1815 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr83; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1814,7 +1829,7 @@ st53: if ( ++p == pe ) goto _test_eof53; case 53: -#line 1818 "src/memcached-grammar.m" +#line 1833 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st53; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1828,7 +1843,7 @@ st54: if ( ++p == pe ) goto _test_eof54; case 54: -#line 1832 "src/memcached-grammar.m" +#line 1847 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr87; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1842,7 +1857,7 @@ st55: if ( ++p == pe ) goto _test_eof55; case 55: -#line 1846 "src/memcached-grammar.m" +#line 1861 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st55; if ( 48 <= (*p) && (*p) <= 57 ) @@ -1856,7 +1871,7 @@ st56: if ( ++p == pe ) goto _test_eof56; case 56: -#line 1860 "src/memcached-grammar.m" +#line 1875 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr91; case 13: goto tr92; @@ -1866,7 +1881,7 @@ case 56: goto st56; goto st0; tr106: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st57; tr92: @@ -1877,7 +1892,7 @@ st57: if ( ++p == pe ) goto _test_eof57; case 57: -#line 1881 "src/memcached-grammar.m" +#line 1896 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr95; goto st0; @@ -1889,7 +1904,7 @@ st58: if ( ++p == pe ) goto _test_eof58; case 58: -#line 1893 "src/memcached-grammar.m" +#line 1908 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -1963,14 +1978,14 @@ case 65: } goto st0; tr107: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st66; st66: if ( ++p == pe ) goto _test_eof66; case 66: -#line 1974 "src/memcached-grammar.m" +#line 1989 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr95; case 13: goto st57; @@ -2014,18 +2029,18 @@ case 70: goto tr113; goto st0; tr113: -#line 297 "src/memcached-grammar.rl" +#line 298 "src/memcached-grammar.rl" {incr_sign = -1;} goto st71; tr202: -#line 296 "src/memcached-grammar.rl" +#line 297 "src/memcached-grammar.rl" {incr_sign = 1; } goto st71; st71: if ( ++p == pe ) goto _test_eof71; case 71: -#line 2029 "src/memcached-grammar.m" +#line 2044 "src/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st71; @@ -2051,7 +2066,7 @@ st72: if ( ++p == pe ) goto _test_eof72; case 72: -#line 2055 "src/memcached-grammar.m" +#line 2070 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st73; goto st0; @@ -2072,7 +2087,7 @@ st74: if ( ++p == pe ) goto _test_eof74; case 74: -#line 2076 "src/memcached-grammar.m" +#line 2091 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr118; case 13: goto tr119; @@ -2082,7 +2097,7 @@ case 74: goto st74; goto st0; tr133: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st75; tr119: @@ -2093,7 +2108,7 @@ st75: if ( ++p == pe ) goto _test_eof75; case 75: -#line 2097 "src/memcached-grammar.m" +#line 2112 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr122; goto st0; @@ -2105,7 +2120,7 @@ st76: if ( ++p == pe ) goto _test_eof76; case 76: -#line 2109 "src/memcached-grammar.m" +#line 2124 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2179,14 +2194,14 @@ case 83: } goto st0; tr134: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st84; st84: if ( ++p == pe ) goto _test_eof84; case 84: -#line 2190 "src/memcached-grammar.m" +#line 2205 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr122; case 13: goto st75; @@ -2256,7 +2271,7 @@ st90: if ( ++p == pe ) goto _test_eof90; case 90: -#line 2260 "src/memcached-grammar.m" +#line 2275 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2272,14 +2287,14 @@ tr147: } goto st91; tr158: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st91; st91: if ( ++p == pe ) goto _test_eof91; case 91: -#line 2283 "src/memcached-grammar.m" +#line 2298 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr141; goto st0; @@ -2305,7 +2320,7 @@ st93: if ( ++p == pe ) goto _test_eof93; case 93: -#line 2309 "src/memcached-grammar.m" +#line 2324 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr146; case 13: goto tr147; @@ -2326,7 +2341,7 @@ st94: if ( ++p == pe ) goto _test_eof94; case 94: -#line 2330 "src/memcached-grammar.m" +#line 2345 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2400,14 +2415,14 @@ case 101: } goto st0; tr159: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st102; st102: if ( ++p == pe ) goto _test_eof102; case 102: -#line 2411 "src/memcached-grammar.m" +#line 2426 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr141; case 13: goto st91; @@ -2495,7 +2510,7 @@ case 111: } goto st0; tr186: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st112; tr175: @@ -2506,7 +2521,7 @@ st112: if ( ++p == pe ) goto _test_eof112; case 112: -#line 2510 "src/memcached-grammar.m" +#line 2525 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr169; goto st0; @@ -2532,7 +2547,7 @@ st114: if ( ++p == pe ) goto _test_eof114; case 114: -#line 2536 "src/memcached-grammar.m" +#line 2551 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr174; case 13: goto tr175; @@ -2549,7 +2564,7 @@ st115: if ( ++p == pe ) goto _test_eof115; case 115: -#line 2553 "src/memcached-grammar.m" +#line 2568 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2623,14 +2638,14 @@ case 122: } goto st0; tr187: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st123; st123: if ( ++p == pe ) goto _test_eof123; case 123: -#line 2634 "src/memcached-grammar.m" +#line 2649 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr169; case 13: goto st112; @@ -2666,18 +2681,18 @@ case 126: } goto st0; tr191: -#line 293 "src/memcached-grammar.rl" +#line 294 "src/memcached-grammar.rl" {show_cas = false;} goto st127; tr198: -#line 294 "src/memcached-grammar.rl" +#line 295 "src/memcached-grammar.rl" {show_cas = true;} goto st127; st127: if ( ++p == pe ) goto _test_eof127; case 127: -#line 2681 "src/memcached-grammar.m" +#line 2696 "src/memcached-grammar.m" switch( (*p) ) { case 13: goto st0; case 32: goto st127; @@ -2703,7 +2718,7 @@ st128: if ( ++p == pe ) goto _test_eof128; case 128: -#line 2707 "src/memcached-grammar.m" +#line 2722 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr195; case 13: goto st129; @@ -2963,7 +2978,7 @@ st156: if ( ++p == pe ) goto _test_eof156; case 156: -#line 2967 "src/memcached-grammar.m" +#line 2982 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st157; goto st0; @@ -2984,7 +2999,7 @@ st158: if ( ++p == pe ) goto _test_eof158; case 158: -#line 2988 "src/memcached-grammar.m" +#line 3003 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr225; if ( 48 <= (*p) && (*p) <= 57 ) @@ -2998,7 +3013,7 @@ st159: if ( ++p == pe ) goto _test_eof159; case 159: -#line 3002 "src/memcached-grammar.m" +#line 3017 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st159; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3012,7 +3027,7 @@ st160: if ( ++p == pe ) goto _test_eof160; case 160: -#line 3016 "src/memcached-grammar.m" +#line 3031 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr229; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3030,7 +3045,7 @@ st161: if ( ++p == pe ) goto _test_eof161; case 161: -#line 3034 "src/memcached-grammar.m" +#line 3049 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st161; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3044,7 +3059,7 @@ st162: if ( ++p == pe ) goto _test_eof162; case 162: -#line 3048 "src/memcached-grammar.m" +#line 3063 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr233; case 13: goto tr234; @@ -3058,14 +3073,14 @@ tr234: {bytes = natoq(fstart, p);} goto st163; tr247: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st163; st163: if ( ++p == pe ) goto _test_eof163; case 163: -#line 3069 "src/memcached-grammar.m" +#line 3084 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr237; goto st0; @@ -3077,7 +3092,7 @@ st164: if ( ++p == pe ) goto _test_eof164; case 164: -#line 3081 "src/memcached-grammar.m" +#line 3096 "src/memcached-grammar.m" switch( (*p) ) { case 32: goto st164; case 78: goto st165; @@ -3203,7 +3218,7 @@ st176: if ( ++p == pe ) goto _test_eof176; case 176: -#line 3207 "src/memcached-grammar.m" +#line 3222 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st177; goto st0; @@ -3224,7 +3239,7 @@ st178: if ( ++p == pe ) goto _test_eof178; case 178: -#line 3228 "src/memcached-grammar.m" +#line 3243 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr255; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3238,7 +3253,7 @@ st179: if ( ++p == pe ) goto _test_eof179; case 179: -#line 3242 "src/memcached-grammar.m" +#line 3257 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st179; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3252,7 +3267,7 @@ st180: if ( ++p == pe ) goto _test_eof180; case 180: -#line 3256 "src/memcached-grammar.m" +#line 3271 "src/memcached-grammar.m" if ( (*p) == 32 ) goto tr259; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3270,7 +3285,7 @@ st181: if ( ++p == pe ) goto _test_eof181; case 181: -#line 3274 "src/memcached-grammar.m" +#line 3289 "src/memcached-grammar.m" if ( (*p) == 32 ) goto st181; if ( 48 <= (*p) && (*p) <= 57 ) @@ -3284,7 +3299,7 @@ st182: if ( ++p == pe ) goto _test_eof182; case 182: -#line 3288 "src/memcached-grammar.m" +#line 3303 "src/memcached-grammar.m" switch( (*p) ) { case 10: goto tr263; case 13: goto tr264; @@ -3298,14 +3313,14 @@ tr264: {bytes = natoq(fstart, p);} goto st183; tr277: -#line 282 "src/memcached-grammar.rl" +#line 283 "src/memcached-grammar.rl" { noreply = true; } goto st183; st183: if ( ++p == pe ) goto _test_eof183; case 183: -#line 3309 "src/memcached-grammar.m" +#line 3324 "src/memcached-grammar.m" if ( (*p) == 10 ) goto tr267; goto st0; @@ -3317,7 +3332,7 @@ st184: if ( ++p == pe ) goto _test_eof184; case 184: -#line 3321 "src/memcached-grammar.m" +#line 3336 "src/memcached-grammar.m" switch( (*p) ) { case 32: goto st184; case 78: goto st185; @@ -3632,7 +3647,7 @@ case 196: _out: {} } -#line 307 "src/memcached-grammar.rl" +#line 308 "src/memcached-grammar.rl" if (!done) { diff --git a/src/memcached-grammar.rl b/src/memcached-grammar.rl index cce61f3b22b74b1f4dab435f389c9740c9c04750..1c73641cb43b193e297ae4b7fa1a2d83ac450bed 100644 --- a/src/memcached-grammar.rl +++ b/src/memcached-grammar.rl @@ -252,8 +252,9 @@ memcached_dispatch(struct ev_io *coio, struct iobuf *iobuf) action read_data { 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; + size_t to_read = bytes + 2 - (pe - p); + if (coio_bread(coio, in, to_read) < to_read) + return -1; /* premature EOF */ } /* * Buffered read may have reallocated the diff --git a/src/palloc.m b/src/palloc.m index d811768c479f448cd377231002c16c5b1848c2d3..2d7831026e8968995cd313da8aee0a4caa56605c 100644 --- a/src/palloc.m +++ b/src/palloc.m @@ -42,8 +42,6 @@ #include <tbuf.h> #include "exception.h" -#define PALLOC_POOL_NAME_MAXLEN 30 - struct chunk { uint32_t magic; void *brk; diff --git a/src/replica.m b/src/replica.m index 208d95e9a9bd137b8ef5a9391beb7522b6604168..60b7a40bff3c8de1f379c1e77f81eac58892ecb1 100644 --- a/src/replica.m +++ b/src/replica.m @@ -51,7 +51,8 @@ remote_read_row(struct ev_io *coio, struct iobuf *iobuf) coio_breadn(coio, in, to_read); } - ssize_t request_len = ((struct header_v11 *)in->pos)->len + sizeof(struct header_v11); + 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) @@ -69,6 +70,8 @@ static void remote_connect(struct ev_io *coio, struct sockaddr_in *remote_addr, i64 initial_lsn, const char **err) { + evio_socket(coio, AF_INET, SOCK_STREAM, IPPROTO_TCP); + *err = "can't connect to master"; coio_connect(coio, remote_addr); @@ -95,13 +98,13 @@ pull_from_remote(va_list ap) bool warning_said = false; const int reconnect_delay = 1; - evio_clear(&coio); + coio_init(&coio); for (;;) { const char *err = NULL; @try { fiber_setcancellable(true); - if (! evio_is_connected(&coio)) { + if (! evio_is_active(&coio)) { if (iobuf == NULL) iobuf = iobuf_create(fiber->name); remote_connect(&coio, &r->remote->addr, diff --git a/src/sio.m b/src/sio.m index 7ac6bfaf5fee70774878a75bc697c6b8118b0086..74b2189ce65ae3b52d6dd5239bba1a0ac1982b7a 100644 --- a/src/sio.m +++ b/src/sio.m @@ -61,21 +61,26 @@ sio_socketname(int fd) return name; } - @implementation SocketError -- (id) init: (int) fd in: (const char *) format, ... +- (id) init: (int) fd in: (const char *) format: (va_list) ap { int save_errno = errno; char buf[TNT_ERRMSG_MAX]; - va_list ap; - va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); const char *socketname = sio_socketname(fd); errno = save_errno; self = [self init: "%s, called on %s", buf, socketname]; return self; } + +- (id) init: (int) fd in: (const char *) format, ... +{ + va_list ap; + va_start(ap, format); + self = [self init: fd :format :ap]; + va_end(ap); + return self; +} @end /** Get a string representation of a socket option name, @@ -97,12 +102,22 @@ sio_option_name(int option) #undef CASE_OPTION } +/** shut down part of a full-duplex connection */ +int +sio_shutdown(int fd, int how) +{ + int rc = shutdown(fd, how); + if (rc < 0) + tnt_raise(SocketError, :fd in:"shutdown"); + return rc; +} + /** Try to automatically configure a listen backlog. * On Linux, use the system setting, which defaults * to 128. This way a system administrator can tune * the backlog as needed. On other systems, use SOMAXCONN. */ -static int +int sio_listen_backlog() { #ifdef TARGET_OS_LINUX @@ -120,9 +135,9 @@ sio_listen_backlog() /** Create a TCP socket. */ int -sio_socket(void) +sio_socket(int domain, int type, int protocol) { - int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + int fd = socket(domain, type, protocol); if (fd < 0) tnt_raise(SocketError, :fd in:"socket"); return fd; @@ -179,8 +194,9 @@ sio_connect(int fd, struct sockaddr_in *addr, socklen_t addrlen) { /* Establish the connection. */ int rc = connect(fd, (struct sockaddr *) addr, addrlen); - if (rc < 0 && errno != EINPROGRESS) + if (rc < 0 && errno != EINPROGRESS) { tnt_raise(SocketError, :fd in:"connect"); + } return rc; } @@ -250,6 +266,30 @@ sio_writev(int fd, const struct iovec *iov, int iovcnt) return n; } +/** Send a message on a socket. */ +ssize_t +sio_sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr_in *dest_addr, socklen_t addrlen) +{ + ssize_t n = sendto(fd, buf, len, flags, dest_addr, addrlen); + if (n < 0 && errno != EAGAIN && + errno != EWOULDBLOCK && errno != EINTR) + tnt_raise(SocketError, :fd in:"sendto(%zd)", len); + return n; +} + +/** Receive a message on a socket. */ +ssize_t +sio_recvfrom(int fd, void *buf, size_t len, int flags, + struct sockaddr_in *src_addr, socklen_t *addrlen) +{ + ssize_t n = recvfrom(fd, buf, len, flags, src_addr, addrlen); + if (n < 0 && errno != EAGAIN && + errno != EWOULDBLOCK && errno != EINTR) + tnt_raise(SocketError, :fd in:"recvfrom(%zd)", len); + return n; +} + /** Get socket peer name. */ int sio_getpeername(int fd, struct sockaddr_in *addr) @@ -276,4 +316,3 @@ sio_strfaddr(struct sockaddr_in *addr) inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); return name; } - diff --git a/src/tarantool.m b/src/tarantool.m index ea54b0dac6d27f1b0293024b4f6e1f5ef6999a57..fc1f955807ccbb6e2bc1ea21448012a669fb1668 100644 --- a/src/tarantool.m +++ b/src/tarantool.m @@ -594,9 +594,8 @@ tarantool_free(void) unlink(cfg.pid_file); destroy_tarantool_cfg(&cfg); - fiber_free(); - coeio_free(); session_free(); + fiber_free(); palloc_free(); ev_default_destroy(); #ifdef ENABLE_GCOV diff --git a/test/big/iterator.result b/test/big/iterator.result index 117481a34bc27fce90e26daa497e4fd584b4481b..adb61724241eda93ceb1fcf7eb2dcc45e7740091 100644 --- a/test/big/iterator.result +++ b/test/big/iterator.result @@ -830,9 +830,9 @@ lua iterate(20, 4, 0, 1, box.index.GE, 'pid_001') --- sorted output $pid_001$ -$pid_002$ -$pid_005$ -$pid_017$ +$pid_007$ +$pid_011$ +$pid_019$ $pid_023$ ... lua iterate(20, 4, 0, 1, box.index.GE, 'pid_999') diff --git a/test/big/sql.result b/test/big/sql.result index 1fe0688f60a0436916546eeb9268980255b7d387..222809b43f52e4786f68133acaa5a56555725067 100644 --- a/test/big/sql.result +++ b/test/big/sql.result @@ -84,9 +84,9 @@ insert into t1 values ('key3', 'part1', 'part2_b') Insert OK, 1 row affected lua for k, v in box.space[1]:pairs() do print(v) end --- -830039403: {'part1', 'part2'} -863593835: {'part1', 'part2_b'} 846816619: {'part1', 'part2_a'} +863593835: {'part1', 'part2_b'} +830039403: {'part1', 'part2'} ... select * from t1 where k0='key1' Found 1 tuple: @@ -108,8 +108,10 @@ Found 3 tuples: [846816619, 'part1', 'part2_a'] [863593835, 'part1', 'part2_b'] call box.select_range(1, 0, 100, 'key2') -Found 1 tuple: +Found 3 tuples: +[830039403, 'part1', 'part2'] [846816619, 'part1', 'part2_a'] +[863593835, 'part1', 'part2_b'] call box.select_range(1, 1, 100, 'part1', 'part2_a') Found 2 tuples: [846816619, 'part1', 'part2_a'] @@ -336,9 +338,9 @@ insert into t4 values(3, 'Creature ') Insert OK, 1 row affected lua for k, v in box.space[4]:pairs() do print(v) end --- +2: {'Bilimbi'} 3: {'Creature '} 1: {'Aardvark '} -2: {'Bilimbi'} ... lua box.space[4].index[0].idx:min() --- diff --git a/test/box/lua.result b/test/box/lua.result index b6064e5f4ebd90cc6481997bc670e5b928bd7214..77fab6ea919ef47c01d41d8690cad03b60e768f5 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -15,33 +15,34 @@ lua for n in pairs(box) do print(' - box.', n) end - box.fiber - box.space - box.cfg + - box.on_reload_configuration - box.time64 - box.uuid - - box.select_limit + - box.ipc - box.delete - - box.on_reload_configuration - - box.replace - box.bless_space + - box.replace - box.counter + - box.auto_increment - box.time - box.select_range - box.insert - - box.auto_increment - box.update + - box.select_reverse_range - box.info - box.session - box.uuid_hex - - box.select_reverse_range + - box.select - box.slab - box.process - - box.select - box.dostring + - box.select_limit - box.stat - box.flags - box.unpack - - box.ipc - - box.index - box.pack + - box.index + - box.socket ... lua box.pack() --- diff --git a/test/box/socket.result b/test/box/socket.result new file mode 100644 index 0000000000000000000000000000000000000000..bf4be1d81b251bec09333f176ece380c0ad2c756 --- /dev/null +++ b/test/box/socket.result @@ -0,0 +1,914 @@ +lua s = box.socket.udp() +--- +... +lua type(s) +--- + - userdata +... +lua s:close() +--- +... +lua s = box.socket.tcp() +--- +... +lua type(s) +--- + - userdata +... +lua s:close() +--- +... +lua s:close() +--- +... +lua s:error() +--- + - 0 + - Success +... +lua s:connect('localhost', '30303') +--- + - nil + - error + - 111 + - Connection refused +... +lua s:error() +--- + - 111 + - Connection refused +... +lua s:connect('127.0.0.1', '30303') +--- + - nil + - error + - 111 + - Connection refused +... +lua s:error() +--- + - 111 + - Connection refused +... +lua s:connect('::1', '30303') +--- + - nil + - error + - -1 + - Host name resolution failed +... +lua s:error() +--- + - -1 + - Host name resolution failed +... +lua s:connect('127.0.0.1', '30303', 0.01) +--- + - nil + - error + - 111 + - Connection refused +... +lua s:error() +--- + - 111 + - Connection refused +... +lua s:connect('127.0.0.1') +--- +error: 'bad argument #3 to ''?'' (string expected, got no value)' +... +lua s:connect() +--- +error: 'bad argument #2 to ''?'' (string expected, got no value)' +... +lua s:connect(123) +--- +error: 'bad argument #3 to ''?'' (string expected, got no value)' +... +lua s:close() +--- +... +lua s:close() +--- +... +lua s:connect('somewhereelse', '30303', 0.01) +--- + - nil + - timeout + - 110 + - Connection timed out +... +lua s:error() +--- + - 110 + - Connection timed out +... +lua s:close() +--- +... +lua s:send() +--- +error: 'box.socket: socket is not initialized' +... +lua s:send(1) +--- +error: 'box.socket: socket is not initialized' +... +lua s:error() +--- + - 110 + - Connection timed out +... +lua s = box.socket.tcp() +--- +... +lua type(s:connect('127.0.0.1', '30303')) +--- + - userdata +... +lua s:send('ping') +--- + - 4 +... +lua s:error() +--- + - 0 + - Success +... +lua n, status, error_code, error_str = s:send(string.rep('=', 200000), 0.0000001) +--- +... +lua type(n) +--- + - number +... +lua type(status) +--- + - string +... +lua type(error_code) +--- + - number +... +lua type(error_str) +--- + - string +... +lua n +--- + - 163840 +... +lua status +--- + - timeout +... +lua error_code +--- + - 110 +... +lua error_str +--- + - Connection timed out +... +lua s:error() +--- + - 110 + - Connection timed out +... +connected +lua s:send('ping') +--- + - 0 + - error + - 104 + - Connection reset by peer +... +lua s:error() +--- + - 104 + - Connection reset by peer +... +lua s:close() +--- +... +lua s:recv() +--- +error: 'box.socket: socket is not initialized' +... +lua type(s:connect('127.0.0.1', '30308')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +12 +lua s:recv(12) +--- + - Hello, World +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30308')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +21 +lua s:recv(11) +--- + - Hello World +... +lua s:recv(5) +--- + - Over +... +lua s:recv(5) +--- + - sized +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30308')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +connected +3 +lua s:recv(4, 0.01) +--- + - + - timeout + - 110 + - Connection timed out +... +lua s:error() +--- + - 110 + - Connection timed out +... +lua s:recv(4) +--- + - ping +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30309')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +connected +4 +lua s:recv(6) +--- + - ping + - eof +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30311')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +connected +24 +lua s:recv(5) +--- + - ping +... +lua s:recv(5) +--- + - ping +... +lua s:recv(5) +--- + - ping +... +lua s:recv(5) +--- + - ping +... +lua s:recv(5) +--- + - end + - eof +... +lua s:recv(5) +--- + - + - eof +... +lua s:recv(5) +--- + - + - eof +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30305')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +12 +lua s:readline() +--- + - Hello World + + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +12 +lua s:readline(5) +--- + - Hello + - limit +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(5, 0.01) +--- + - Worl + - limit +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(6, 0.01) +--- + - d + + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +9 +lua s:readline({'i', 'D'}) +--- + - AbcD + - nil + - 2 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'i', 'G'}) +--- + - efG + - nil + - 2 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'i'}) +--- + - hi + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +13 +lua s:readline({'Cat', 'Cow', 'Dog', 'Star'}) +--- + - Cat + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'Cat', 'Cow', 'Dog', 'Star'}) +--- + - Cow + - nil + - 2 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'Cat', 'Cow', 'Dog', 'Star'}) +--- + - Dog + - nil + - 3 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'Cat', 'Cow', 'Dog', 'Star'}) +--- + - Star + - nil + - 4 +... +lua s:error() +--- + - 0 + - Success +... +15 +lua s:readline(3, {'Cat', 'Coow'}) +--- + - Cat + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(3, {'Cat', 'Coow'}) +--- + - Coo + - limit +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(3, {'Dogg', 'Star'}) +--- + - wDo + - limit +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(3, {'Dogg', 'Star'}) +--- + - ggS + - limit +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(3) +--- + - tar + - limit +... +lua s:error() +--- + - 0 + - Success +... +23 +lua sl = {'Crown', 'King', 'Kong', 'Cezar'} +--- +... +lua s:readline(sl, 1.0) +--- + - KKong + - nil + - 3 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(sl, 1.0) +--- + - King + - nil + - 2 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(sl, 1.0) +--- + - CezaCezar + - nil + - 4 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(sl, 1.0) +--- + - Crown + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +26 +lua sl = {'Agatha', 'Road', 'Corn', 'Drive', 'Pop'} +--- +... +lua s:readline(64, sl, 1.0) +--- + - RoAgatha + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(64, sl, 1.0) +--- + - Pop + - nil + - 5 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(64, sl, 1.0) +--- + - PoCorn + - nil + - 3 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline(64, sl, 1.0) +--- + - Drive + - nil + - 4 +... +lua s:error() +--- + - 0 + - Success +... +21 +lua s:readline({'Canada'}, 0.01) +--- + - + - timeout + - 110 + - Connection timed out +... +lua s:error() +--- + - 110 + - Connection timed out +... +2 +lua s:readline({'Canada'}, 0.01) +--- + - RoadAfricaCubaRomaniaCanada + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +6 +lua s:readline({'Canada'}, 0.01) +--- + - Canada + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +19 +lua s:readline({'msg'}) +--- + - msg + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - msg + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - msg + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - msg + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - msg + - nil + - 1 +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - + - eof +... +lua s:error() +--- + - 0 + - Success +... +lua s:readline({'msg'}) +--- + - + - eof +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('127.0.0.1', '30307')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +29 +lua s:readline({'Z'}) +--- + - SomelongLongStringStrinString + - eof +... +lua s:error() +--- + - 0 + - Success +... +lua s:close() +--- +... +lua type(s:connect('localhost', '30303')) +--- + - userdata +... +lua s:send('ping') +--- + - 4 +... +connected +lua s:recv(4) +--- + - ping +... +lua s:send('ping') +--- + - 4 +... +lua s:error() +--- + - 0 + - Success +... +lua s:send('ping') +--- + - 0 + - error + - 32 + - Broken pipe +... +lua s:error() +--- + - 32 + - Broken pipe +... +lua s:close() +--- +... +lua type(s:bind('localhost', '30303')) +--- + - userdata +... +lua type(s:listen()) +--- + - userdata +... +lua client, status, addr = s:accept() +--- +... +lua addr +--- + - 127.0.0.1 +... +lua data = client:recv(4) +--- +... +lua data +--- + - ping +... +lua client:send(data, 4) +--- + - 4 +... +lua client:close() +--- +... +lua s:close() +--- +... +ping +lua s = box.socket.udp() +--- +... +lua type(s:sendto('ping', 'localhost', '30302')) +--- + - number +... +lua s:error() +--- + - 0 + - Success +... +ping +lua s:recv(4) +--- + - + - error + - 107 + - Transport endpoint is not connected +... +lua s:close() +--- +... +lua s = box.socket.udp() +--- +... +lua type(s:bind('localhost', '30301')) +--- + - userdata +... +lua s:error() +--- + - 0 + - Success +... +lua data, status, client, port = s:recvfrom(4) +--- +... +lua s:error() +--- + - 0 + - Success +... +lua data +--- + - ping +... +lua client +--- + - 127.0.0.1 +... +lua type(s:sendto(data, client, port)) +--- + - number +... +lua s:error() +--- + - 0 + - Success +... +ping +lua s:close() +--- +... diff --git a/test/box/socket.test b/test/box/socket.test new file mode 100644 index 0000000000000000000000000000000000000000..91e4e5d4f5e1980b8a5d7f1aa575baabd390bb18 --- /dev/null +++ b/test/box/socket.test @@ -0,0 +1,511 @@ +# encoding: tarantool + +import socket + +######################## +# # +# box.socket.tcp/udp() # +# # +######################## + +exec admin "lua s = box.socket.udp()" +exec admin "lua type(s)" +exec admin "lua s:close()" +exec admin "lua s = box.socket.tcp()" +exec admin "lua type(s)" + +### socket:close() + +# close +exec admin "lua s:close()" + +# close (on closed) +exec admin "lua s:close()" + +# error +exec admin "lua s:error()" + +#################### +# # +# socket:connect() # +# # +#################### + +exec admin "lua s:connect('localhost', '30303')" +# Connection refused +exec admin "lua s:error()" + +exec admin "lua s:connect('127.0.0.1', '30303')" +# Connection refused +exec admin "lua s:error()" + +exec admin "lua s:connect('::1', '30303')" +# connection refused +exec admin "lua s:error()" + +exec admin "lua s:connect('127.0.0.1', '30303', 0.01)" +# connection refused +exec admin "lua s:error()" + +# bad args +exec admin "lua s:connect('127.0.0.1')" +exec admin "lua s:connect()" +exec admin "lua s:connect(123)" +exec admin "lua s:close()" + +exec admin "lua s:close()" +exec admin "lua s:connect('somewhereelse', '30303', 0.01)" +# timedout +exec admin "lua s:error()" +exec admin "lua s:close()" + +################## +# # +# socket:send() # +# # +################## + +# bad args +exec admin "lua s:send()" +exec admin "lua s:send(1)" +# bad file descriptor +exec admin "lua s:error()" + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30303)) +s.listen(1) + +exec admin "lua s = box.socket.tcp()" +exec admin "lua type(s:connect('127.0.0.1', '30303'))" +exec admin "lua s:send('ping')" +exec admin "lua s:error()" + +# timedout +exec admin "lua n, status, error_code, error_str = s:send(string.rep('=', 200000), 0.0000001)" +exec admin "lua type(n)" +exec admin "lua type(status)" +exec admin "lua type(error_code)" +exec admin "lua type(error_str)" +exec admin "lua n" +exec admin "lua status" +exec admin "lua error_code" +exec admin "lua error_str" + +exec admin "lua s:error()" + +conn, addr = s.accept() +print('connected') + +conn.close() +s.close() + +# connection reset by peer +exec admin "lua s:send('ping')" +exec admin "lua s:error()" + +exec admin "lua s:close()" + +################# +# # +# socket:recv() # +# # +################# + +# bad args +exec admin "lua s:recv()" + +# test for case #1: successful recv +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30308)) +s.listen(1) +exec admin "lua type(s:connect('127.0.0.1', '30308'))" +exec admin "lua s:error()" +conn, addr = s.accept() +print(conn.send("Hello, World")) +exec admin "lua s:recv(12)" +exec admin "lua s:error()" +exec admin "lua s:close()" +conn.close() +s.close() + +# test for case #1: successful serial chunk recv (3 x chunk) +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30308)) +s.listen(1) +exec admin "lua type(s:connect('127.0.0.1', '30308'))" +exec admin "lua s:error()" +conn, addr = s.accept() +print(conn.send("Hello World Oversized")) +exec admin "lua s:recv(11)" +exec admin "lua s:recv(5)" +exec admin "lua s:recv(5)" +exec admin "lua s:error()" +exec admin "lua s:close()" +conn.close() +s.close() + +# test for case #2-3: timedout, error +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30308)) +s.listen(1) + +exec admin "lua type(s:connect('127.0.0.1', '30308'))" +exec admin "lua s:error()" + +conn, addr = s.accept() +print('connected') + +print(conn.send("pin")) +exec admin "lua s:recv(4, 0.01)" +# timedout +exec admin "lua s:error()" + +conn.send("g") + +# ping +exec admin "lua s:recv(4)" +exec admin "lua s:error()" + +exec admin "lua s:close()" +conn.close() +s.close() + +# test for case #4: EOF (data is less then recv size) +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30309)) +s.listen(1) + +exec admin "lua type(s:connect('127.0.0.1', '30309'))" +exec admin "lua s:error()" + +conn, addr = s.accept() +print('connected') + +print(conn.send("ping")) +conn.close() +exec admin "lua s:recv(6)" +exec admin "lua s:error()" +exec admin "lua s:close()" +s.close() + +# test for case #4: EOF (data is more then recv size) +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30311)) +s.listen(1) + +exec admin "lua type(s:connect('127.0.0.1', '30311'))" +exec admin "lua s:error()" + +conn, addr = s.accept() +print('connected') + +print(conn.send("ping ping ping ping end ")) +conn.close() + +# recv should not say EOF, even if it really happened +exec admin "lua s:recv(5)" +exec admin "lua s:recv(5)" +exec admin "lua s:recv(5)" +exec admin "lua s:recv(5)" + +# eof +exec admin "lua s:recv(5)" +# eof (zero) +exec admin "lua s:recv(5)" +# eof (zero) +exec admin "lua s:recv(5)" + +exec admin "lua s:error()" +exec admin "lua s:close()" +s.close() + +##################### +# # +# socket:readline() # +# # +##################### + +# possible usage: +# +# 1. readline() == readline(limit == inf, seplist == {'\n'}, timeout == inf) +# 2. readline(limit) +# 3. readline(limit, timeout) +# 4. readline({seplist}) +# 5. readline(limit, {seplist}) +# 6. readline({seplist}, timeout) +# 7. readline(limit, {seplist}, timeout) + +# test for case #1, 4: separator or limit found + different usage +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30305)) +s.listen(1) +exec admin "lua type(s:connect('127.0.0.1', '30305'))" +exec admin "lua s:error()" +conn, addr = s.accept() + +# 1. readline() == readline(limit == inf, seplist == {'\n'}, timeout == inf) +print(conn.send("Hello World\n")) +exec admin "lua s:readline()" +exec admin "lua s:error()" + +# 2. readline(limit) +print(conn.send("Hello World\n")) +exec admin "lua s:readline(5)" +exec admin "lua s:error()" + +# 3. readline(limit, timeout) (just api check) +exec admin "lua s:readline(5, 0.01)" +exec admin "lua s:error()" +exec admin "lua s:readline(6, 0.01)" +exec admin "lua s:error()" + +# 4. readline({seplist}) +print(conn.send("AbcDefGhi")) +exec admin "lua s:readline({'i', 'D'})" +exec admin "lua s:error()" +exec admin "lua s:readline({'i', 'G'})" +exec admin "lua s:error()" +exec admin "lua s:readline({'i'})" +exec admin "lua s:error()" + +print(conn.send("CatCowDogStar")) +exec admin "lua s:readline({'Cat', 'Cow', 'Dog', 'Star'})" +exec admin "lua s:error()" +exec admin "lua s:readline({'Cat', 'Cow', 'Dog', 'Star'})" +exec admin "lua s:error()" +exec admin "lua s:readline({'Cat', 'Cow', 'Dog', 'Star'})" +exec admin "lua s:error()" +exec admin "lua s:readline({'Cat', 'Cow', 'Dog', 'Star'})" +exec admin "lua s:error()" + +# 5. readline(limit, {seplist}) +print(conn.send("CatCoowDoggStar")) +exec admin "lua s:readline(3, {'Cat', 'Coow'})" +exec admin "lua s:error()" +exec admin "lua s:readline(3, {'Cat', 'Coow'})" +exec admin "lua s:error()" +exec admin "lua s:readline(3, {'Dogg', 'Star'})" +exec admin "lua s:error()" +exec admin "lua s:readline(3, {'Dogg', 'Star'})" +exec admin "lua s:error()" + +# read 'tar' part +exec admin "lua s:readline(3)" +exec admin "lua s:error()" + +# 6. readline({seplist}, timeout) +print(conn.send("KKongKingCezaCezarCrown")) +exec admin "lua sl = {'Crown', 'King', 'Kong', 'Cezar'}" +exec admin "lua s:readline(sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(sl, 1.0)" +exec admin "lua s:error()" + +# 7. readline(limit, {seplist}, timeout) +print(conn.send("RoAgathaPopPoCornDriveRoad")) +exec admin "lua sl = {'Agatha', 'Road', 'Corn', 'Drive', 'Pop'}" +exec admin "lua s:readline(64, sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(64, sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(64, sl, 1.0)" +exec admin "lua s:error()" +exec admin "lua s:readline(64, sl, 1.0)" +exec admin "lua s:error()" + +# test for case #2-3: timedout, errors +# +print(conn.send("AfricaCubaRomaniaCana")) +exec admin "lua s:readline({'Canada'}, 0.01)" +# timedout +exec admin "lua s:error()" + +print(conn.send("da")) +# should read whole line +exec admin "lua s:readline({'Canada'}, 0.01)" +exec admin "lua s:error()" + +# to ensure readahead pointer correctness +print(conn.send("Canada")) +exec admin "lua s:readline({'Canada'}, 0.01)" +exec admin "lua s:error()" + +# test for case #5: eof testing +# +print(conn.send("msg msg msg msg msg")) +conn.close() + +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +# eof (zero) +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +# eof (zero) +exec admin "lua s:readline({'msg'})" +exec admin "lua s:error()" + +exec admin "lua s:close()" + +s.close() + +# test for case #5: eof in the middle of a separator +# pending +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30307)) +s.listen(1) +exec admin "lua type(s:connect('127.0.0.1', '30307'))" +exec admin "lua s:error()" +conn, addr = s.accept() + +print(conn.send("SomelongLongStringStrinString")) +conn.close() + +# should be returned with eof flag +exec admin "lua s:readline({'Z'})" +exec admin "lua s:error()" + +exec admin "lua s:close()" + +s.close() + +########################### +# simple usage as testing # +########################### + +# echo server (test is server) (TCP). +# +# connect from stored procedure to echo server and +# do send/recv. +# +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30303)) +s.listen(1) + +exec admin "lua type(s:connect('localhost', '30303'))" +exec admin "lua s:send('ping')" + +conn, addr = s.accept() +print('connected') +data = conn.recv(4) +conn.sendall(data) + +exec admin "lua s:recv(4)" +conn.close() +s.close() + +exec admin "lua s:send('ping')" +exec admin "lua s:error()" + +# broken pipe +exec admin "lua s:send('ping')" +exec admin "lua s:error()" + +exec admin "lua s:close()" + +# echo server (box is server) (TCP). +# +# connect from test to echo server implemented in +# stored procedure and do send/recv. +# +exec admin "lua type(s:bind('localhost', '30303'))" +exec admin "lua type(s:listen())" + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.connect(('localhost', 30303)) +s.sendall('ping') + +exec admin "lua client, status, addr = s:accept()" +exec admin "lua addr" +exec admin "lua data = client:recv(4)" +exec admin "lua data" +exec admin "lua client:send(data, 4)" +exec admin "lua client:close()" +exec admin "lua s:close()" + +data = s.recv(4) +s.close() +print(data) + +# echo server (test is server) (UDP). +# +# connect from stored procedure to echo server and +# do send/recv. +# +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(('localhost', 30302)) + +# SOCK_DGRAM +exec admin "lua s = box.socket.udp()" +exec admin "lua type(s:sendto('ping', 'localhost', '30302'))" +exec admin "lua s:error()" + +data, addr = s.recvfrom(4) +print(data) +s.sendto(data, addr) + +exec admin "lua s:recv(4)" + +s.close() +exec admin "lua s:close()" + +# echo server (box is server) (UDP). +# +# connect from test to echo server implemented in +# stored procedure and do send/recv. +# +exec admin "lua s = box.socket.udp()" +exec admin "lua type(s:bind('localhost', '30301'))" +exec admin "lua s:error()" + +s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +s.sendto('ping', ('127.0.0.1', 30301)) + +exec admin "lua data, status, client, port = s:recvfrom(4)" +exec admin "lua s:error()" +exec admin "lua data" +exec admin "lua client" + +exec admin "lua type(s:sendto(data, client, port))" +exec admin "lua s:error()" + +data = s.recv(4) +print(data) +s.close() + +exec admin "lua s:close()" diff --git a/test/unit/bit.c b/test/unit/bit.c index 9b305369efd3253bc5201aaefc34d6662579397b..a7cce03672e5333f53edf8bbc3ad26a64ca4b54e 100644 --- a/test/unit/bit.c +++ b/test/unit/bit.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <inttypes.h> #include <assert.h> +#include <stdlib.h> #include "unit.h" diff --git a/third_party/PMurHash.c b/third_party/PMurHash.c new file mode 100644 index 0000000000000000000000000000000000000000..017501264ddff28d8521ef841e000c456f5358ef --- /dev/null +++ b/third_party/PMurHash.c @@ -0,0 +1,317 @@ +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/*----------------------------------------------------------------------------- + +If you want to understand the MurmurHash algorithm you would be much better +off reading the original source. Just point your browser at: +http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + + +What this version provides? + +1. Progressive data feeding. Useful when the entire payload to be hashed +does not fit in memory or when the data is streamed through the application. +Also useful when hashing a number of strings with a common prefix. A partial +hash of a prefix string can be generated and reused for each suffix string. + +2. Portability. Plain old C so that it should compile on any old compiler. +Both CPU endian and access-alignment neutral, but avoiding inefficient code +when possible depending on CPU capabilities. + +3. Drop in. I personally like nice self contained public domain code, making it +easy to pilfer without loads of refactoring to work properly in the existing +application code & makefile structure and mucking around with licence files. +Just copy PMurHash.h and PMurHash.c and you're ready to go. + + +How does it work? + +We can only process entire 32 bit chunks of input, except for the very end +that may be shorter. So along with the partial hash we need to give back to +the caller a carry containing up to 3 bytes that we were unable to process. +This carry also needs to record the number of bytes the carry holds. I use +the low 2 bits as a count (0..3) and the carry bytes are shifted into the +high byte in stream order. + +To handle endianess I simply use a macro that reads a uint32_t and define +that macro to be a direct read on little endian machines, a read and swap +on big endian machines, or a byte-by-byte read if the endianess is unknown. + +-----------------------------------------------------------------------------*/ + + +#include "PMurHash.h" + +/* I used ugly type names in the header to avoid potential conflicts with + * application or system typedefs & defines. Since I'm not including any more + * headers below here I can rename these so that the code reads like C99 */ +#undef uint32_t +#define uint32_t MH_UINT32 +#undef uint8_t +#define uint8_t MH_UINT8 + +/* MSVC warnings we choose to ignore */ +#if defined(_MSC_VER) + #pragma warning(disable: 4127) /* conditional expression is constant */ +#endif + +/*----------------------------------------------------------------------------- + * Endianess, misalignment capabilities and util macros + * + * The following 3 macros are defined in this section. The other macros defined + * are only needed to help derive these 3. + * + * READ_UINT32(x) Read a little endian unsigned 32-bit int + * UNALIGNED_SAFE Defined if READ_UINT32 works on non-word boundaries + * ROTL32(x,r) Rotate x left by r bits + */ + +/* Convention is to define __BYTE_ORDER == to one of these values */ +#if !defined(__BIG_ENDIAN) + #define __BIG_ENDIAN 4321 +#endif +#if !defined(__LITTLE_ENDIAN) + #define __LITTLE_ENDIAN 1234 +#endif + +/* I386 */ +#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(i386) + #define __BYTE_ORDER __LITTLE_ENDIAN + #define UNALIGNED_SAFE +#endif + +/* gcc 'may' define __LITTLE_ENDIAN__ or __BIG_ENDIAN__ to 1 (Note the trailing __), + * or even _LITTLE_ENDIAN or _BIG_ENDIAN (Note the single _ prefix) */ +#if !defined(__BYTE_ORDER) + #if defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__==1 || defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN==1 + #define __BYTE_ORDER __LITTLE_ENDIAN + #elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 || defined(_BIG_ENDIAN) && _BIG_ENDIAN==1 + #define __BYTE_ORDER __BIG_ENDIAN + #endif +#endif + +/* gcc (usually) defines xEL/EB macros for ARM and MIPS endianess */ +#if !defined(__BYTE_ORDER) + #if defined(__ARMEL__) || defined(__MIPSEL__) + #define __BYTE_ORDER __LITTLE_ENDIAN + #endif + #if defined(__ARMEB__) || defined(__MIPSEB__) + #define __BYTE_ORDER __BIG_ENDIAN + #endif +#endif + +/* Now find best way we can to READ_UINT32 */ +#if __BYTE_ORDER==__LITTLE_ENDIAN + /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ + #define READ_UINT32(ptr) (*((uint32_t*)(ptr))) +#elif __BYTE_ORDER==__BIG_ENDIAN + /* TODO: Add additional cases below where a compiler provided bswap32 is available */ + #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) + #define READ_UINT32(ptr) (__builtin_bswap32(*((uint32_t*)(ptr)))) + #else + /* Without a known fast bswap32 we're just as well off doing this */ + #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + #define UNALIGNED_SAFE + #endif +#else + /* Unknown endianess so last resort is to read individual bytes */ + #define READ_UINT32(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) + + /* Since we're not doing word-reads we can skip the messing about with realignment */ + #define UNALIGNED_SAFE +#endif + +/* Find best way to ROTL32 */ +#if defined(_MSC_VER) + #include <stdlib.h> /* Microsoft put _rotl declaration in here */ + #define ROTL32(x,r) _rotl(x,r) +#else + /* gcc recognises this code and generates a rotate instruction for CPUs with one */ + #define ROTL32(x,r) (((uint32_t)x << r) | ((uint32_t)x >> (32 - r))) +#endif + + +/*----------------------------------------------------------------------------- + * Core murmurhash algorithm macros */ + +#define C1 (0xcc9e2d51) +#define C2 (0x1b873593) + +/* This is the main processing body of the algorithm. It operates + * on each full 32-bits of input. */ +#define DOBLOCK(h1, k1) do{ \ + k1 *= C1; \ + k1 = ROTL32(k1,15); \ + k1 *= C2; \ + \ + h1 ^= k1; \ + h1 = ROTL32(h1,13); \ + h1 = h1*5+0xe6546b64; \ + }while(0) + + +/* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ +/* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ +#define DOBYTES(cnt, h1, c, n, ptr, len) do{ \ + int _i = cnt; \ + while(_i--) { \ + c = c>>8 | *ptr++<<24; \ + n++; len--; \ + if(n==4) { \ + DOBLOCK(h1, c); \ + n = 0; \ + } \ + } }while(0) + +/*---------------------------------------------------------------------------*/ + +/* Main hashing function. Initialise carry to 0 and h1 to 0 or an initial seed + * if wanted. Both ph1 and pcarry are required arguments. */ +void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int len) +{ + uint32_t h1 = *ph1; + uint32_t c = *pcarry; + + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end; + + /* Extract carry count from low 2 bits of c value */ + int n = c & 3; + +#if defined(UNALIGNED_SAFE) + /* This CPU handles unaligned word access */ + + /* Consume any carry bytes */ + int i = (4-n) & 3; + if(i && i <= len) { + DOBYTES(i, h1, c, n, ptr, len); + } + + /* Process 32-bit chunks */ + end = ptr + len/4*4; + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = READ_UINT32(ptr); + DOBLOCK(h1, k1); + } + +#else /*UNALIGNED_SAFE*/ + /* This CPU does not handle unaligned word access */ + + /* Consume enough so that the next data byte is word aligned */ + int i = -(long)ptr & 3; + if(i && i <= len) { + DOBYTES(i, h1, c, n, ptr, len); + } + + /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ + end = ptr + len/4*4; + switch(n) { /* how many bytes in c */ + case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = READ_UINT32(ptr); + DOBLOCK(h1, k1); + } + break; + case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>24; + c = READ_UINT32(ptr); + k1 |= c<<8; + DOBLOCK(h1, k1); + } + break; + case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>16; + c = READ_UINT32(ptr); + k1 |= c<<16; + DOBLOCK(h1, k1); + } + break; + case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ + for( ; ptr < end ; ptr+=4) { + uint32_t k1 = c>>8; + c = READ_UINT32(ptr); + k1 |= c<<24; + DOBLOCK(h1, k1); + } + } +#endif /*UNALIGNED_SAFE*/ + + /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ + len -= len/4*4; + + /* Append any remaining bytes into carry */ + DOBYTES(len, h1, c, n, ptr, len); + + /* Copy out new running hash and carry */ + *ph1 = h1; + *pcarry = (c & ~0xff) | n; +} + +/*---------------------------------------------------------------------------*/ + +/* Finalize a hash. To match the original Murmur3A the total_length must be provided */ +uint32_t PMurHash32_Result(uint32_t h, uint32_t carry, uint32_t total_length) +{ + uint32_t k1; + int n = carry & 3; + if(n) { + k1 = carry >> (4-n)*8; + k1 *= C1; k1 = ROTL32(k1,15); k1 *= C2; h ^= k1; + } + h ^= total_length; + + /* fmix */ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +/*---------------------------------------------------------------------------*/ + +/* Murmur3A compatable all-at-once */ +uint32_t PMurHash32(uint32_t seed, const void *key, int len) +{ + uint32_t h1=seed, carry=0; + PMurHash32_Process(&h1, &carry, key, len); + return PMurHash32_Result(h1, carry, len); +} + +/*---------------------------------------------------------------------------*/ + +/* Provide an API suitable for smhasher */ +void PMurHash32_test(const void *key, int len, uint32_t seed, void *out) +{ + uint32_t h1=seed, carry=0; + const uint8_t *ptr = (uint8_t*)key; + const uint8_t *end = ptr + len; + +#if 0 /* Exercise the progressive processing */ + while(ptr < end) { + //const uint8_t *mid = ptr + rand()%(end-ptr)+1; + const uint8_t *mid = ptr + (rand()&0xF); + mid = mid<end?mid:end; + PMurHash32_Process(&h1, &carry, ptr, mid-ptr); + ptr = mid; + } +#else + PMurHash32_Process(&h1, &carry, ptr, (int)(end-ptr)); +#endif + h1 = PMurHash32_Result(h1, carry, len); + *(uint32_t*)out = h1; +} + +/*---------------------------------------------------------------------------*/ diff --git a/third_party/PMurHash.h b/third_party/PMurHash.h new file mode 100644 index 0000000000000000000000000000000000000000..28ead00a7de1d52231f8d2026a3de5e8d76e5ae5 --- /dev/null +++ b/third_party/PMurHash.h @@ -0,0 +1,64 @@ +/*----------------------------------------------------------------------------- + * MurmurHash3 was written by Austin Appleby, and is placed in the public + * domain. + * + * This implementation was written by Shane Day, and is also public domain. + * + * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) + * with support for progressive processing. + */ + +/* ------------------------------------------------------------------------- */ +/* Determine what native type to use for uint32_t */ + +/* We can't use the name 'uint32_t' here because it will conflict with + * any version provided by the system headers or application. */ + +/* First look for special cases */ +#if defined(_MSC_VER) + #define MH_UINT32 unsigned long +#endif + +/* If the compiler says it's C99 then take its word for it */ +#if !defined(MH_UINT32) && ( \ + defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L ) + #include <stdint.h> + #define MH_UINT32 uint32_t +#endif + +/* Otherwise try testing against max value macros from limit.h */ +#if !defined(MH_UINT32) + #include <limits.h> + #if (USHRT_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned short + #elif (UINT_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned int + #elif (ULONG_MAX == 0xffffffffUL) + #define MH_UINT32 unsigned long + #endif +#endif + +#if !defined(MH_UINT32) + #error Unable to determine type name for unsigned 32-bit int +#endif + +/* I'm yet to work on a platform where 'unsigned char' is not 8 bits */ +#define MH_UINT8 unsigned char + + +/* ------------------------------------------------------------------------- */ +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +void PMurHash32_Process(MH_UINT32 *ph1, MH_UINT32 *pcarry, const void *key, int len); +MH_UINT32 PMurHash32_Result(MH_UINT32 h1, MH_UINT32 carry, MH_UINT32 total_length); +MH_UINT32 PMurHash32(MH_UINT32 seed, const void *key, int len); + +void PMurHash32_test(const void *key, int len, MH_UINT32 seed, void *out); + +#ifdef __cplusplus +} +#endif diff --git a/third_party/README b/third_party/README index 7fb70f87e8d83ce21cb4b3fabd482480c23bda5c..abf30f28929d178fc20a98792a4e243a663aab54 100644 --- a/third_party/README +++ b/third_party/README @@ -34,8 +34,27 @@ How to update it: - merge our Makefile with the Makefile in the source tarball + +How to update libeio +==================== +Note: we have two important changes to the stock libeio: +- it's possible to change the libeio thread stack size + with EIO_STACKSIZE constant. In particular, EIO_STACKSIZE + set to 0 means the default stack size. + A larger stack size is necessary to make getaddrinfo() + work. + +Otherwise: cvs up + How to update rb.h ====================== Get the header from git://canonware.com/jemalloc.git apply rb.patch patch +======= + +How to update murmur hash +========================= + +wget http://smhasher.googlecode.com/svn/trunk/PMurHash.c -O PMurHash.c +wget http://smhasher.googlecode.com/svn/trunk/PMurHash.h -O PMurHash.h diff --git a/third_party/libeio/xthread.h b/third_party/libeio/xthread.h index 2a89f45921fb082a89163532bef647195e7bbdef..60310d59cc024d44a930b1f5cced053c18a201f9 100644 --- a/third_party/libeio/xthread.h +++ b/third_party/libeio/xthread.h @@ -140,7 +140,8 @@ xthread_create (xthread_t *tid, void *(*proc)(void *), void *arg) pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); - pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN < X_STACKSIZE ? X_STACKSIZE : PTHREAD_STACK_MIN); + if (X_STACKSIZE != 0) + pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN < X_STACKSIZE ? X_STACKSIZE : PTHREAD_STACK_MIN); #ifdef PTHREAD_SCOPE_PROCESS pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); #endif diff --git a/third_party/murmur_hash2.c b/third_party/murmur_hash2.c deleted file mode 100644 index 6c64bace58d7cd19a81f0f2920cac0aaf498d9ff..0000000000000000000000000000000000000000 --- a/third_party/murmur_hash2.c +++ /dev/null @@ -1,64 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2, by Austin Appleby - -// Note - This code makes a few assumptions about how your machine behaves - - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 - -// And it has a few limitations - - -// 1. It will not work incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. - -static inline unsigned int MurmurHash2 ( const void * key, int len, unsigned int seed ) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - - unsigned int h = seed ^ len; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data = (const unsigned char *)key; - - while(len >= 4) - { - unsigned int k = *(unsigned int *)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - - switch(len) - { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} diff --git a/third_party/tarantool_ev.c b/third_party/tarantool_ev.c index f7c47bcccf99a035230cbb1929568e566356ef6f..763de0292e921f6e70db6d3b09ae1dedf25e4642 100644 --- a/third_party/tarantool_ev.c +++ b/third_party/tarantool_ev.c @@ -26,5 +26,8 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include "tarantool_ev.h" #include "third_party/libev/ev.c" + +const ev_tstamp TIMEOUT_INFINITY = 365*86400*100.0; diff --git a/third_party/tarantool_ev.h b/third_party/tarantool_ev.h index 949f535e66375b43feae9a15a2f2285c1bc86d1e..ccd8ed4d01dffabc30ede95e940ad8b0056f51d7 100644 --- a/third_party/tarantool_ev.h +++ b/third_party/tarantool_ev.h @@ -52,5 +52,6 @@ #else /* !defined(ENABLE_BUNDLED_LIBEV) */ #include <ev.h> #endif +extern const ev_tstamp TIMEOUT_INFINITY; #endif /* TARANTOOL_EV_H_INCLUDED */