From f5c4671bfbad96bf346bd7e9a21fc4317b4959df Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Sat, 3 Dec 2022 17:00:20 +0530 Subject: Adds most of the tools --- v_windows/v/thirdparty/picoev/picoev.c | 9 + v_windows/v/thirdparty/picoev/src/README.md | 21 ++ v_windows/v/thirdparty/picoev/src/picoev.h | 404 ++++++++++++++++++++++ v_windows/v/thirdparty/picoev/src/picoev_epoll.c | 168 +++++++++ v_windows/v/thirdparty/picoev/src/picoev_kqueue.c | 209 +++++++++++ v_windows/v/thirdparty/picoev/src/picoev_select.c | 169 +++++++++ v_windows/v/thirdparty/picoev/src/picoev_w32.h | 15 + 7 files changed, 995 insertions(+) create mode 100644 v_windows/v/thirdparty/picoev/picoev.c create mode 100644 v_windows/v/thirdparty/picoev/src/README.md create mode 100644 v_windows/v/thirdparty/picoev/src/picoev.h create mode 100644 v_windows/v/thirdparty/picoev/src/picoev_epoll.c create mode 100644 v_windows/v/thirdparty/picoev/src/picoev_kqueue.c create mode 100644 v_windows/v/thirdparty/picoev/src/picoev_select.c create mode 100644 v_windows/v/thirdparty/picoev/src/picoev_w32.h (limited to 'v_windows/v/thirdparty/picoev') diff --git a/v_windows/v/thirdparty/picoev/picoev.c b/v_windows/v/thirdparty/picoev/picoev.c new file mode 100644 index 0000000..bd6691a --- /dev/null +++ b/v_windows/v/thirdparty/picoev/picoev.c @@ -0,0 +1,9 @@ +#ifdef __linux__ + #include "src/picoev_epoll.c" +#elif __APPLE__ + #include "src/picoev_kqueue.c" +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + #include "src/picoev_kqueue.c" +#else + #include "src/picoev_select.c" +#endif diff --git a/v_windows/v/thirdparty/picoev/src/README.md b/v_windows/v/thirdparty/picoev/src/README.md new file mode 100644 index 0000000..9619e08 --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/README.md @@ -0,0 +1,21 @@ +picoev +====== + +A *tiny*, *lightning fast* event loop for network applications + +The text below is copied from the [original publication](http://developer.cybozu.co.jp/archives/kazuho/2009/08/picoev-a-tiny-e.html) + +I am sure many programmers writing network applications have their own abstracting layers hiding the differences between various I/O multiplex APIs, like select(2), poll(2), epoll(2), ... And of course, I am one among them. While writing mycached ([see Mycached: memcached protocol support for MySQL for more information](http://developer.cybozu.co.jp/archives/kazuho/2009/08/mycached-memcac.html)), I was at first considering of using [libev](http://software.schmorp.de/pkg/libev.html) for multiplexing socket I/Os. [Libevent](http://www.monkey.org/~provos/libevent/) was not an option since it does not (yet) provide multithreading support. + +But it was a great pain for me to learn how to use libev. I do not mean that its is an ugly product. In fact, I think that it is a very well written, excellent library. However, for me it was too much a boring task to learn how the things are abstracted, already being familiar with the problems it tries to hide. + +So instead I thought it might be a good occasion to write my own library that could be used in any programs I may write in the future. The result is picoev, and it is faster than libevent or libev! The benchmark used is a re-modified version taken from libev.schmorp.de/bench.html and can be found [here](http://coderepos.org/share/browser/lang/c/picoev/trunk/example/bench.c). +![setup time](http://developer.cybozu.co.jp/archives/kazuho/files/picoev_setup.png) +![event processing time](http://developer.cybozu.co.jp/archives/kazuho/files/picoev_event.png) + +Why is it faster? It is because it uses an array and a ring buffer of bit vectors as its internal structure. Libevent and libev seem to use some kind of sorted tree to represent file descriptors. However, if we concentrate on Un*x systems, there is a guarantee that the descriptors will be a small positive integer. Picoev utilizes the fact and stores information related to file descriptors (such as pointers to callback functions or callback arguments) in an array, resulting in a faster manipulation of socket states. + +Another optimization technique used by picoev is not to use an ordered tree for keeping timeout information. Generally speaking, most network applications do not require accurate timeouts. Thus it is possible to use a ring buffer (a sliding array) of bit vectors for the purpose. Each bit vector represents a set of file descriptors that time-outs at a given time. Picoev uses 128 of bit vectors to represent timeouts, for example, the first bit vector represents the sockets that timeout a second after, the second bit vector representing them of two seconds after..., and the bit vectors slide every second. If the maximum timeout required by the web application is greater than 128, the minimum granurality of timeout becomes two seconds. + +I would like to reiterate that both libevent and libev are great libraries. Picoev is not at comparable to them especially in maturity and the number of features. It only supports select(2), epoll(2), and kqueue(2) for the time being. However the design is simple than the other two, and I think it will be a good starting point to write network applications, or to use as a basis for writing one's own network libraries. + diff --git a/v_windows/v/thirdparty/picoev/src/picoev.h b/v_windows/v/thirdparty/picoev/src/picoev.h new file mode 100644 index 0000000..b00b02b --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/picoev.h @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE 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. + */ + +#ifndef picoev_h +#define picoev_h + +#ifdef __cplusplus +extern "C" { +# define PICOEV_INLINE inline +#else +# define PICOEV_INLINE static __inline__ +#endif + +#include +#include +#include +#include +#include + +#define PICOEV_IS_INITED (picoev.max_fd != 0) +#define PICOEV_IS_INITED_AND_FD_IN_RANGE(fd) \ + (((unsigned)fd) < (unsigned)picoev.max_fd) +#define PICOEV_TOO_MANY_LOOPS (picoev.num_loops != 0) /* use after ++ */ +#define PICOEV_FD_BELONGS_TO_LOOP(loop, fd) \ + ((loop)->loop_id == picoev.fds[fd].loop_id) + +#define PICOEV_TIMEOUT_VEC_OF(loop, idx) \ + ((loop)->timeout.vec + (idx) * picoev.timeout_vec_size) +#define PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, idx) \ + ((loop)->timeout.vec_of_vec + (idx) * picoev.timeout_vec_of_vec_size) +#define PICOEV_RND_UP(v, d) (((v) + (d) - 1) / (d) * (d)) + +#define PICOEV_PAGE_SIZE 4096 +#define PICOEV_CACHE_LINE_SIZE 32 /* in bytes, ok if greater than the actual */ +#define PICOEV_SIMD_BITS 128 +#define PICOEV_TIMEOUT_VEC_SIZE 128 +#define PICOEV_SHORT_BITS (sizeof(short) * 8) + +#define PICOEV_READ 1 +#define PICOEV_WRITE 2 +#define PICOEV_TIMEOUT 4 +#define PICOEV_ADD 0x40000000 +#define PICOEV_DEL 0x20000000 +#define PICOEV_READWRITE (PICOEV_READ | PICOEV_WRITE) + +#define PICOEV_TIMEOUT_IDX_UNUSED (UCHAR_MAX) + + typedef unsigned short picoev_loop_id_t; + + typedef struct picoev_loop_st picoev_loop; + + typedef void picoev_handler(picoev_loop* loop, int fd, int revents, + void* cb_arg); + + typedef struct picoev_fd_st { + /* use accessors! */ + /* TODO adjust the size to match that of a cache line */ + picoev_handler* callback; + void* cb_arg; + picoev_loop_id_t loop_id; + char events; + unsigned char timeout_idx; /* PICOEV_TIMEOUT_IDX_UNUSED if not used */ + int _backend; /* can be used by backends (never modified by core) */ + } picoev_fd; + + struct picoev_loop_st { + /* read only */ + picoev_loop_id_t loop_id; + struct { + short* vec; + short* vec_of_vec; + size_t base_idx; + time_t base_time; + int resolution; + void* _free_addr; + } timeout; + time_t now; + }; + + typedef struct picoev_globals_st { + /* read only */ + picoev_fd* fds; + void* _fds_free_addr; + int max_fd; + int num_loops; + size_t timeout_vec_size; /* # of elements in picoev_loop.timeout.vec[0] */ + size_t timeout_vec_of_vec_size; /* ... in timeout.vec_of_vec[0] */ + } picoev_globals; + + extern picoev_globals picoev; + + /* creates a new event loop (defined by each backend) */ + picoev_loop* picoev_create_loop(int max_timeout); + + /* destroys a loop (defined by each backend) */ + int picoev_destroy_loop(picoev_loop* loop); + + /* internal: updates events to be watched (defined by each backend) */ + int picoev_update_events_internal(picoev_loop* loop, int fd, int events); + + /* internal: poll once and call the handlers (defined by each backend) */ + int picoev_poll_once_internal(picoev_loop* loop, int max_wait); + + /* internal, aligned allocator with address scrambling to avoid cache + line contention */ + PICOEV_INLINE + void* picoev_memalign(size_t sz, void** orig_addr, int clear) { + sz = sz + PICOEV_PAGE_SIZE + PICOEV_CACHE_LINE_SIZE; + if ((*orig_addr = malloc(sz)) == NULL) { + return NULL; + } + if (clear != 0) { + memset(*orig_addr, 0, sz); + } + return + (void*)PICOEV_RND_UP((unsigned long)*orig_addr + + (rand() % PICOEV_PAGE_SIZE), + PICOEV_CACHE_LINE_SIZE); + } + + /* initializes picoev */ + PICOEV_INLINE + int picoev_init(int max_fd) { + assert(! PICOEV_IS_INITED); + assert(max_fd > 0); + if ((picoev.fds = (picoev_fd*)picoev_memalign(sizeof(picoev_fd) * max_fd, + &picoev._fds_free_addr, 1)) + == NULL) { + return -1; + } + picoev.max_fd = max_fd; + picoev.num_loops = 0; + picoev.timeout_vec_size + = PICOEV_RND_UP(picoev.max_fd, PICOEV_SIMD_BITS) / PICOEV_SHORT_BITS; + picoev.timeout_vec_of_vec_size + = PICOEV_RND_UP(picoev.timeout_vec_size, PICOEV_SIMD_BITS) + / PICOEV_SHORT_BITS; + return 0; + } + + /* deinitializes picoev */ + PICOEV_INLINE + int picoev_deinit(void) { + assert(PICOEV_IS_INITED); + free(picoev._fds_free_addr); + picoev.fds = NULL; + picoev._fds_free_addr = NULL; + picoev.max_fd = 0; + picoev.num_loops = 0; + return 0; + } + + /* updates timeout */ + PICOEV_INLINE + void picoev_set_timeout(picoev_loop* loop, int fd, int secs) { + picoev_fd* target; + short* vec, * vec_of_vec; + size_t vi = fd / PICOEV_SHORT_BITS, delta; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + assert(PICOEV_FD_BELONGS_TO_LOOP(loop, fd)); + target = picoev.fds + fd; + /* clear timeout */ + if (target->timeout_idx != PICOEV_TIMEOUT_IDX_UNUSED) { + vec = PICOEV_TIMEOUT_VEC_OF(loop, target->timeout_idx); + if ((vec[vi] &= ~((unsigned short)SHRT_MIN >> (fd % PICOEV_SHORT_BITS))) + == 0) { + vec_of_vec = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, target->timeout_idx); + vec_of_vec[vi / PICOEV_SHORT_BITS] + &= ~((unsigned short)SHRT_MIN >> (vi % PICOEV_SHORT_BITS)); + } + target->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + } + if (secs != 0) { + delta = (loop->now + secs - loop->timeout.base_time) + / loop->timeout.resolution; + if (delta >= PICOEV_TIMEOUT_VEC_SIZE) { + delta = PICOEV_TIMEOUT_VEC_SIZE - 1; + } + target->timeout_idx = + (loop->timeout.base_idx + delta) % PICOEV_TIMEOUT_VEC_SIZE; + vec = PICOEV_TIMEOUT_VEC_OF(loop, target->timeout_idx); + vec[vi] |= (unsigned short)SHRT_MIN >> (fd % PICOEV_SHORT_BITS); + vec_of_vec = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, target->timeout_idx); + vec_of_vec[vi / PICOEV_SHORT_BITS] + |= (unsigned short)SHRT_MIN >> (vi % PICOEV_SHORT_BITS); + } + } + + /* registers a file descriptor and callback argument to a event loop */ + PICOEV_INLINE + int picoev_add(picoev_loop* loop, int fd, int events, int timeout_in_secs, + picoev_handler* callback, void* cb_arg) { + picoev_fd* target; + if (!PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)) { return -1; } + target = picoev.fds + fd; + assert(target->loop_id == 0); + target->callback = callback; + target->cb_arg = cb_arg; + target->loop_id = loop->loop_id; + target->events = 0; + target->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + if (picoev_update_events_internal(loop, fd, events | PICOEV_ADD) != 0) { + target->loop_id = 0; + return -1; + } + picoev_set_timeout(loop, fd, timeout_in_secs); + return 0; + } + + /* unregisters a file descriptor from event loop */ + PICOEV_INLINE + int picoev_del(picoev_loop* loop, int fd) { + picoev_fd* target; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + target = picoev.fds + fd; + if (picoev_update_events_internal(loop, fd, PICOEV_DEL) != 0) { + return -1; + } + picoev_set_timeout(loop, fd, 0); + target->loop_id = 0; + return 0; + } + + /* check if fd is registered (checks all loops if loop == NULL) */ + PICOEV_INLINE + int picoev_is_active(picoev_loop* loop, int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return loop != NULL + ? picoev.fds[fd].loop_id == loop->loop_id + : picoev.fds[fd].loop_id != 0; + } + + /* returns events being watched for given descriptor */ + PICOEV_INLINE + int picoev_get_events(picoev_loop* loop __attribute__((unused)), int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return picoev.fds[fd].events & PICOEV_READWRITE; + } + + /* sets events to be watched for given desriptor */ + PICOEV_INLINE + int picoev_set_events(picoev_loop* loop, int fd, int events) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (picoev.fds[fd].events != events + && picoev_update_events_internal(loop, fd, events) != 0) { + return -1; + } + return 0; + } + + /* returns callback for given descriptor */ + PICOEV_INLINE + picoev_handler* picoev_get_callback(picoev_loop* loop __attribute__((unused)), + int fd, void** cb_arg) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (cb_arg != NULL) { + *cb_arg = picoev.fds[fd].cb_arg; + } + return picoev.fds[fd].callback; + } + + /* sets callback for given descriptor */ + PICOEV_INLINE + void picoev_set_callback(picoev_loop* loop __attribute__((unused)), int fd, + picoev_handler* callback, void** cb_arg) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (cb_arg != NULL) { + picoev.fds[fd].cb_arg = *cb_arg; + } + picoev.fds[fd].callback = callback; + } + + /* function to iterate registered information. To start iteration, set curfd + to -1 and call the function until -1 is returned */ + PICOEV_INLINE + int picoev_next_fd(picoev_loop* loop, int curfd) { + if (curfd != -1) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(curfd)); + } + while (++curfd < picoev.max_fd) { + if (loop->loop_id == picoev.fds[curfd].loop_id) { + return curfd; + } + } + return -1; + } + + /* internal function */ + PICOEV_INLINE + int picoev_init_loop_internal(picoev_loop* loop, int max_timeout) { + loop->loop_id = ++picoev.num_loops; + assert(PICOEV_TOO_MANY_LOOPS); + if ((loop->timeout.vec_of_vec + = (short*)picoev_memalign((picoev.timeout_vec_of_vec_size + + picoev.timeout_vec_size) + * sizeof(short) * PICOEV_TIMEOUT_VEC_SIZE, + &loop->timeout._free_addr, 1)) + == NULL) { + --picoev.num_loops; + return -1; + } + loop->timeout.vec = loop->timeout.vec_of_vec + + picoev.timeout_vec_of_vec_size * PICOEV_TIMEOUT_VEC_SIZE; + loop->timeout.base_idx = 0; + loop->timeout.base_time = time(NULL); + loop->timeout.resolution + = PICOEV_RND_UP(max_timeout, PICOEV_TIMEOUT_VEC_SIZE) + / PICOEV_TIMEOUT_VEC_SIZE; + return 0; + } + + /* internal function */ + PICOEV_INLINE + void picoev_deinit_loop_internal(picoev_loop* loop) { + free(loop->timeout._free_addr); + } + + /* internal function */ + PICOEV_INLINE + void picoev_handle_timeout_internal(picoev_loop* loop) { + size_t i, j, k; + for (; + loop->timeout.base_time <= loop->now - loop->timeout.resolution; + loop->timeout.base_idx + = (loop->timeout.base_idx + 1) % PICOEV_TIMEOUT_VEC_SIZE, + loop->timeout.base_time += loop->timeout.resolution) { + /* TODO use SIMD instructions */ + short* vec = PICOEV_TIMEOUT_VEC_OF(loop, loop->timeout.base_idx); + short* vec_of_vec + = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, loop->timeout.base_idx); + for (i = 0; i < picoev.timeout_vec_of_vec_size; ++i) { + short vv = vec_of_vec[i]; + if (vv != 0) { + for (j = i * PICOEV_SHORT_BITS; vv != 0; j++, vv <<= 1) { + if (vv < 0) { + short v = vec[j]; + assert(v != 0); + for (k = j * PICOEV_SHORT_BITS; v != 0; k++, v <<= 1) { + if (v < 0) { + picoev_fd* fd = picoev.fds + k; + assert(fd->loop_id == loop->loop_id); + fd->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + (*fd->callback)(loop, k, PICOEV_TIMEOUT, fd->cb_arg); + } + } + vec[j] = 0; + } + } + vec_of_vec[i] = 0; + } + } + } + } + + /* loop once */ + PICOEV_INLINE + int picoev_loop_once(picoev_loop* loop, int max_wait) { + loop->now = time(NULL); + if (max_wait > loop->timeout.resolution) { + max_wait = loop->timeout.resolution; + } + if (picoev_poll_once_internal(loop, max_wait) != 0) { + return -1; + } + if (max_wait != 0) { + loop->now = time(NULL); + } + picoev_handle_timeout_internal(loop); + return 0; + } + +#undef PICOEV_INLINE + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v_windows/v/thirdparty/picoev/src/picoev_epoll.c b/v_windows/v/thirdparty/picoev/src/picoev_epoll.c new file mode 100644 index 0000000..61bda4d --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/picoev_epoll.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE 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 +#include +#include +#include "picoev.h" + +#ifndef PICOEV_EPOLL_DEFER_DELETES +# define PICOEV_EPOLL_DEFER_DELETES 1 +#endif + +typedef struct picoev_loop_epoll_st { + picoev_loop loop; + int epfd; + struct epoll_event events[1024]; +} picoev_loop_epoll; + +picoev_globals picoev; + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop_epoll* loop; + + /* init parent */ + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop_epoll*)malloc(sizeof(picoev_loop_epoll))) == NULL) { + return NULL; + } + if (picoev_init_loop_internal(&loop->loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + /* init myself */ + if ((loop->epfd = epoll_create(picoev.max_fd)) == -1) { + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return NULL; + } + + loop->loop.now = time(NULL); + return &loop->loop; +} + +int picoev_destroy_loop(picoev_loop* _loop) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + + if (close(loop->epfd) != 0) { + return -1; + } + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* _loop, int fd, int events) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + picoev_fd* target = picoev.fds + fd; + struct epoll_event ev; + int epoll_ret; + + memset( &ev, 0, sizeof( ev ) ); + assert(PICOEV_FD_BELONGS_TO_LOOP(&loop->loop, fd)); + + if ((events & PICOEV_READWRITE) == target->events) { + return 0; + } + + ev.events = ((events & PICOEV_READ) != 0 ? EPOLLIN : 0) + | ((events & PICOEV_WRITE) != 0 ? EPOLLOUT : 0); + ev.data.fd = fd; + +#define SET(op, check_error) do { \ + epoll_ret = epoll_ctl(loop->epfd, op, fd, &ev); \ + assert(! check_error || epoll_ret == 0); \ + } while (0) + +#if PICOEV_EPOLL_DEFER_DELETES + + if ((events & PICOEV_DEL) != 0) { + /* nothing to do */ + } else if ((events & PICOEV_READWRITE) == 0) { + SET(EPOLL_CTL_DEL, 1); + } else { + SET(EPOLL_CTL_MOD, 0); + if (epoll_ret != 0) { + assert(errno == ENOENT); + SET(EPOLL_CTL_ADD, 1); + } + } + +#else + + if ((events & PICOEV_READWRITE) == 0) { + SET(EPOLL_CTL_DEL, 1); + } else { + SET(target->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, 1); + } + +#endif + +#undef SET + + target->events = events; + + return 0; +} + +int picoev_poll_once_internal(picoev_loop* _loop, int max_wait) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + int i, nevents; + + nevents = epoll_wait(loop->epfd, loop->events, + sizeof(loop->events) / sizeof(loop->events[0]), + max_wait * 1000); + if (nevents == -1) { + return -1; + } + for (i = 0; i < nevents; ++i) { + struct epoll_event* event = loop->events + i; + picoev_fd* target = picoev.fds + event->data.fd; + if (loop->loop.loop_id == target->loop_id + && (target->events & PICOEV_READWRITE) != 0) { + int revents = ((event->events & EPOLLIN) != 0 ? PICOEV_READ : 0) + | ((event->events & EPOLLOUT) != 0 ? PICOEV_WRITE : 0); + if (revents != 0) { + (*target->callback)(&loop->loop, event->data.fd, revents, + target->cb_arg); + } + } else { +#if PICOEV_EPOLL_DEFER_DELETES + event->events = 0; + epoll_ctl(loop->epfd, EPOLL_CTL_DEL, event->data.fd, event); +#endif + } + } + return 0; +} diff --git a/v_windows/v/thirdparty/picoev/src/picoev_kqueue.c b/v_windows/v/thirdparty/picoev/src/picoev_kqueue.c new file mode 100644 index 0000000..a45a052 --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/picoev_kqueue.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE 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 +#include +#include +#include +#include +#include "picoev.h" + +#define EV_QUEUE_SZ 128 + +#define BACKEND_BUILD(next_fd, events) \ + ((unsigned)((next_fd << 8) | (events & 0xff))) +#define BACKEND_GET_NEXT_FD(backend) ((int)(backend) >> 8) +#define BACKEND_GET_OLD_EVENTS(backend) ((int)(backend) & 0xff) + +typedef struct picoev_loop_kqueue_st { + picoev_loop loop; + int kq; + int changed_fds; /* link list using picoev_fd::_backend, -1 if not changed */ + struct kevent events[1024]; + struct kevent changelist[256]; +} picoev_loop_kqueue; + +picoev_globals picoev; + +static int apply_pending_changes(picoev_loop_kqueue* loop, int apply_all) +{ +#define SET(op, events) \ + EV_SET(loop->changelist + cl_off++, loop->changed_fds, \ + (((events) & PICOEV_READ) != 0 ? EVFILT_READ : 0) \ + | (((events) & PICOEV_WRITE) != 0 ? EVFILT_WRITE : 0), \ + (op), 0, 0, NULL) + + int cl_off = 0, nevents; + + while (loop->changed_fds != -1) { + picoev_fd* changed = picoev.fds + loop->changed_fds; + int old_events = BACKEND_GET_OLD_EVENTS(changed->_backend); + if (changed->events != old_events) { + if (old_events != 0) { + SET(EV_DISABLE, old_events); + } + if (changed->events != 0) { + SET(EV_ADD | EV_ENABLE, changed->events); + } + if ((size_t)cl_off + 1 + >= sizeof(loop->changelist) / sizeof(loop->changelist[0])) { + nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL); + assert(nevents == 0); + cl_off = 0; + } + } + loop->changed_fds = BACKEND_GET_NEXT_FD(changed->_backend); + changed->_backend = -1; + } + + if (apply_all && cl_off != 0) { + nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL); + assert(nevents == 0); + cl_off = 0; + } + + return cl_off; + +#undef SET +} + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop_kqueue* loop; + + /* init parent */ + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop_kqueue*)malloc(sizeof(picoev_loop_kqueue))) + == NULL) { + return NULL; + } + if (picoev_init_loop_internal(&loop->loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + /* init kqueue */ + if ((loop->kq = kqueue()) == -1) { + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return NULL; + } + loop->changed_fds = -1; + + loop->loop.now = time(NULL); + return &loop->loop; +} + +int picoev_destroy_loop(picoev_loop* _loop) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + + if (close(loop->kq) != 0) { + return -1; + } + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* _loop, int fd, int events) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + picoev_fd* target = picoev.fds + fd; + + assert(PICOEV_FD_BELONGS_TO_LOOP(&loop->loop, fd)); + + /* initialize if adding the fd */ + if ((events & PICOEV_ADD) != 0) { + target->_backend = -1; + } + /* return if nothing to do */ + if (events == PICOEV_DEL + ? target->_backend == -1 + : (events & PICOEV_READWRITE) == target->events) { + return 0; + } + /* add to changed list if not yet being done */ + if (target->_backend == -1) { + target->_backend = BACKEND_BUILD(loop->changed_fds, target->events); + loop->changed_fds = fd; + } + /* update events */ + target->events = events & PICOEV_READWRITE; + /* apply immediately if is a DELETE */ + if ((events & PICOEV_DEL) != 0) { + apply_pending_changes(loop, 1); + } + + return 0; +} + +int picoev_poll_once_internal(picoev_loop* _loop, int max_wait) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + struct timespec ts; + int cl_off = 0, nevents, i; + + /* apply pending changes, with last changes stored to loop->changelist */ + cl_off = apply_pending_changes(loop, 0); + + ts.tv_sec = max_wait; + ts.tv_nsec = 0; + nevents = kevent(loop->kq, loop->changelist, cl_off, loop->events, + sizeof(loop->events) / sizeof(loop->events[0]), &ts); + if (nevents == -1) { + /* the errors we can only rescue */ + assert(errno == EACCES || errno == EFAULT || errno == EINTR); + return -1; + } + for (i = 0; i < nevents; ++i) { + struct kevent* event = loop->events + i; + picoev_fd* target = picoev.fds + event->ident; + assert((event->flags & EV_ERROR) == 0); /* changelist errors are fatal */ + if (loop->loop.loop_id == target->loop_id + && (event->filter & (EVFILT_READ | EVFILT_WRITE)) != 0) { + int revents; + switch (event->filter) { + case EVFILT_READ: + revents = PICOEV_READ; + break; + case EVFILT_WRITE: + revents = PICOEV_WRITE; + break; + default: + assert(0); + revents = 0; // suppress compiler warning + break; + } + (*target->callback)(&loop->loop, event->ident, revents, target->cb_arg); + } + } + + return 0; +} diff --git a/v_windows/v/thirdparty/picoev/src/picoev_select.c b/v_windows/v/thirdparty/picoev/src/picoev_select.c new file mode 100644 index 0000000..afbe140 --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/picoev_select.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE 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. + */ + +#ifndef _WIN32 +# include +#else +# include +#endif + +#include "picoev.h" + +#ifdef _WIN32 +# define PICOEV_W32_INTERNAL +# include "picoev_w32.h" +# define PICOEV_FD_SET(x, y) FD_SET(picoev_w32_fd2sock(x), y) +# define PICOEV_FD_ISSET(x, y) FD_ISSET(picoev_w32_fd2sock(x), y) + +typedef struct picoev_w32_globals_st { + int* fds; + void* _fds_free_addr; +} picoev_w32_globals; + +picoev_w32_globals picoev_w32; + +int picoev_w32_sock2fd(int sock) { + int i; + for (i = 0; i < picoev.max_fd && picoev_w32.fds[i]; ++i) + if (picoev_w32.fds[i] == sock) return i; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(i)); + picoev_w32.fds[i] = sock; + return i; +} + +int picoev_w32_fd2sock(int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return picoev_w32.fds[fd]; +} + +extern int picoev_w32_deinit(void); + +int picoev_w32_init(int max_fd) { + int r = picoev_init(max_fd); + if ((picoev_w32.fds = (int*)picoev_memalign(sizeof(int) * max_fd, + &picoev_w32._fds_free_addr, 1)) + == NULL) { + picoev_deinit(); + return -1; + } +} + +int picoev_w32_deinit(void) { + free(picoev_w32._fds_free_addr); + picoev_w32.fds = NULL; + picoev_w32._fds_free_addr = NULL; + return picoev_deinit(); +} + +#else +# define PICOEV_FD_SET(x, y) FD_SET(x, y) +# define PICOEV_FD_ISSET(x, y) FD_ISSET(x, y) +#endif + +picoev_globals picoev; + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop* loop; + + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop*)malloc(sizeof(picoev_loop))) == NULL) { + return NULL; + } + if (picoev_init_loop_internal(loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + loop->now = time(NULL); + return loop; +} + +int picoev_destroy_loop(picoev_loop* loop) +{ + picoev_deinit_loop_internal(loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* loop, int fd, int events) +{ + picoev.fds[fd].events = events & PICOEV_READWRITE; + return 0; +} + +int picoev_poll_once_internal(picoev_loop* loop, int max_wait) +{ + fd_set readfds, writefds, errorfds; + struct timeval tv; + int i, r, maxfd = 0; + + /* setup */ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errorfds); + for (i = 0; i < picoev.max_fd; ++i) { + picoev_fd* fd = picoev.fds + i; + if (fd->loop_id == loop->loop_id) { + if ((fd->events & PICOEV_READ) != 0) { + PICOEV_FD_SET(i, &readfds); + if (maxfd < i) { + maxfd = i; + } + } + if ((fd->events & PICOEV_WRITE) != 0) { + PICOEV_FD_SET(i, &writefds); + if (maxfd < i) { + maxfd = i; + } + } + } + } + + /* select and handle if any */ + tv.tv_sec = max_wait; + tv.tv_usec = 0; + r = select(maxfd + 1, &readfds, &writefds, &errorfds, &tv); + if (r == -1) { + return -1; + } else if (r > 0) { + for (i = 0; i < picoev.max_fd; ++i) { + picoev_fd* target = picoev.fds + i; + if (target->loop_id == loop->loop_id) { + int revents = (PICOEV_FD_ISSET(i, &readfds) ? PICOEV_READ : 0) + | (PICOEV_FD_ISSET(i, &writefds) ? PICOEV_WRITE : 0); + if (revents != 0) { + (*target->callback)(loop, i, revents, target->cb_arg); + } + } + } + } + + return 0; +} diff --git a/v_windows/v/thirdparty/picoev/src/picoev_w32.h b/v_windows/v/thirdparty/picoev/src/picoev_w32.h new file mode 100644 index 0000000..b7e0cc4 --- /dev/null +++ b/v_windows/v/thirdparty/picoev/src/picoev_w32.h @@ -0,0 +1,15 @@ +#ifndef picoev_w32_h +#define picoev_w32_h + +#include "picoev.h" + +#ifndef PICOEV_W32_INTERNAL +extern int picoev_w32_init(int); +extern int picoev_w32_deinit(void); +extern int picoev_w32_sock2fd(int); +extern int picoev_w32_fd2sock(int); +# define picoev_init picoev_w32_init +# define picoev_deinit picoev_w32_deinit +#endif + +#endif -- cgit v1.2.3