diff options
Diffstat (limited to 'src/libmowgli/eventloop')
-rw-r--r-- | src/libmowgli/eventloop/Makefile | 14 | ||||
-rw-r--r-- | src/libmowgli/eventloop/epoll_pollops.c | 198 | ||||
-rw-r--r-- | src/libmowgli/eventloop/eventloop.c | 170 | ||||
-rw-r--r-- | src/libmowgli/eventloop/eventloop.h | 259 | ||||
-rw-r--r-- | src/libmowgli/eventloop/helper.c | 230 | ||||
-rw-r--r-- | src/libmowgli/eventloop/kqueue_pollops.c | 189 | ||||
-rw-r--r-- | src/libmowgli/eventloop/null_pollops.c | 124 | ||||
-rw-r--r-- | src/libmowgli/eventloop/poll_pollops.c | 229 | ||||
-rw-r--r-- | src/libmowgli/eventloop/pollable.c | 88 | ||||
-rw-r--r-- | src/libmowgli/eventloop/ports_pollops.c | 186 | ||||
-rw-r--r-- | src/libmowgli/eventloop/qnx_pollops.c | 200 | ||||
-rw-r--r-- | src/libmowgli/eventloop/select_pollops.c | 203 | ||||
-rw-r--r-- | src/libmowgli/eventloop/timer.c | 172 | ||||
-rw-r--r-- | src/libmowgli/eventloop/windows_pollops.c | 276 |
14 files changed, 2538 insertions, 0 deletions
diff --git a/src/libmowgli/eventloop/Makefile b/src/libmowgli/eventloop/Makefile new file mode 100644 index 0000000..23122a1 --- /dev/null +++ b/src/libmowgli/eventloop/Makefile @@ -0,0 +1,14 @@ +include ../../../extra.mk + +STATIC_PIC_LIB_NOINST = ${LIBMOWGLI_SHARED_EVENTLOOP} +STATIC_LIB_NOINST = ${LIBMOWGLI_STATIC_EVENTLOOP} + +SRCS = eventloop.c helper.c pollable.c timer.c null_pollops.c poll_pollops.c epoll_pollops.c kqueue_pollops.c qnx_pollops.c ports_pollops.c select_pollops.c windows_pollops.c + +INCLUDES = eventloop.h + +include ../../../buildsys.mk + +includesubdir = $(PACKAGE_NAME)/eventloop + +CPPFLAGS += -I. -I.. -I../../.. -DMOWGLI_CORE diff --git a/src/libmowgli/eventloop/epoll_pollops.c b/src/libmowgli/eventloop/epoll_pollops.c new file mode 100644 index 0000000..596b992 --- /dev/null +++ b/src/libmowgli/eventloop/epoll_pollops.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_SYS_EPOLL_H + +#include <sys/epoll.h> + +typedef struct { + int epoll_fd; + int pfd_size; + struct epoll_event *pfd; +} mowgli_epoll_eventloop_private_t; + +static void mowgli_epoll_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_epoll_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_epoll_eventloop_private_t)); + eventloop->poller = priv; + + priv->pfd_size = getdtablesize(); + priv->epoll_fd = epoll_create(priv->pfd_size); + priv->pfd = mowgli_alloc(sizeof(struct epoll_event) * priv->pfd_size); + + return; +} + +static void mowgli_epoll_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_epoll_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + close(priv->epoll_fd); + + mowgli_free(priv->pfd); + mowgli_free(priv); + return; +} + +static void mowgli_epoll_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_epoll_eventloop_private_t *priv; + struct epoll_event ep_event; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + pollable->slot = 0; + + ep_event.events = pollable->slot; + ep_event.data.ptr = pollable; + + if (epoll_ctl(priv->epoll_fd, EPOLL_CTL_DEL, pollable->fd, &ep_event) != 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_epoll_eventloop_destroy(): epoll_ctl failed: %d (%s)", errno, strerror(errno)); + } +} + +static void mowgli_epoll_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_epoll_eventloop_private_t *priv; + struct epoll_event ep_event; + int op = -1; + unsigned int old_flags; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + old_flags = pollable->slot; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + pollable->slot |= EPOLLIN; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + pollable->slot |= EPOLLOUT; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function == NULL) + pollable->slot &= ~EPOLLIN; + + if (pollable->write_function == NULL) + pollable->slot &= ~EPOLLOUT; + + if (old_flags == 0 && pollable->slot == 0) + return; + else if (pollable->slot <= 0) + op = EPOLL_CTL_DEL; + else if (old_flags == 0 && pollable->slot != 0) + op = EPOLL_CTL_ADD; + else if (pollable->slot != old_flags) + op = EPOLL_CTL_MOD; + + if (op == -1) + return; + + ep_event.events = pollable->slot; + ep_event.data.ptr = pollable; + + if (epoll_ctl(priv->epoll_fd, op, pollable->fd, &ep_event) != 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_epoll_eventloop_setselect(): epoll_ctl failed: %d (%s)", errno, strerror(errno)); + } + + return; +} + +static void mowgli_epoll_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + mowgli_epoll_eventloop_private_t *priv; + int i, num, o_errno; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + num = epoll_wait(priv->epoll_fd, priv->pfd, priv->pfd_size, delay); + + o_errno = errno; + mowgli_eventloop_synchronize(eventloop); + + if (num < 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_epoll_eventloop_select(): epoll_wait failed: %d (%s)", o_errno, strerror(o_errno)); + return; + } + + for (i = 0; i < num; i++) + { + mowgli_eventloop_pollable_t *pollable = priv->pfd[i].data.ptr; + + if (priv->pfd[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR) && pollable->read_function != NULL) + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + + if (priv->pfd[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR) && pollable->write_function != NULL) + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + } +} + +mowgli_eventloop_ops_t _mowgli_epoll_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_epoll_eventloop_pollsetup, + .pollshutdown = mowgli_epoll_eventloop_pollshutdown, + .setselect = mowgli_epoll_eventloop_setselect, + .select = mowgli_epoll_eventloop_select, + .destroy = mowgli_epoll_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/eventloop.c b/src/libmowgli/eventloop/eventloop.c new file mode 100644 index 0000000..d2045cd --- /dev/null +++ b/src/libmowgli/eventloop/eventloop.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +static mowgli_heap_t *eventloop_heap = NULL; + +extern mowgli_eventloop_ops_t _mowgli_null_pollops; + +#ifdef HAVE_PORT_CREATE +extern mowgli_eventloop_ops_t _mowgli_ports_pollops; +#endif +#ifdef HAVE_DISPATCH_BLOCK +extern mowgli_eventloop_ops_t _mowgli_qnx_pollops; +#endif +#ifdef HAVE_SELECT +extern mowgli_eventloop_ops_t _mowgli_select_pollops; +#endif +#ifdef HAVE_POLL_H +extern mowgli_eventloop_ops_t _mowgli_poll_pollops; +#endif +#ifdef HAVE_SYS_EPOLL_H +extern mowgli_eventloop_ops_t _mowgli_epoll_pollops; +#endif +#ifdef HAVE_KQUEUE +extern mowgli_eventloop_ops_t _mowgli_kqueue_pollops; +#endif +#if 0 +extern mowgli_eventloop_ops_t _mowgli_winsock_pollops; +#endif + +mowgli_eventloop_t *mowgli_eventloop_create(void) +{ + mowgli_eventloop_t *eventloop; + + if (eventloop_heap == NULL) + eventloop_heap = mowgli_heap_create(sizeof(mowgli_eventloop_t), 16, BH_NOW); + + eventloop = mowgli_heap_alloc(eventloop_heap); + + eventloop->eventloop_ops = &_mowgli_null_pollops; + +#ifdef HAVE_SELECT + eventloop->eventloop_ops = &_mowgli_select_pollops; +#endif +#ifdef HAVE_POLL_H + eventloop->eventloop_ops = &_mowgli_poll_pollops; +#endif +#ifdef HAVE_SYS_EPOLL_H + eventloop->eventloop_ops = &_mowgli_epoll_pollops; +#endif +#ifdef HAVE_KQUEUE + eventloop->eventloop_ops = &_mowgli_kqueue_pollops; +#endif +#ifdef HAVE_DISPATCH_BLOCK + eventloop->eventloop_ops = &_mowgli_qnx_pollops; +#endif +#ifdef HAVE_PORT_CREATE + eventloop->eventloop_ops = &_mowgli_ports_pollops; +#endif +#if 0 + eventloop->eventloop_ops = &_mowgli_winsock_pollops; +#endif + + if (mowgli_mutex_init(&eventloop->mutex) != 0) + { + mowgli_log("couldn't create mutex for eventloop %p, aborting...", eventloop); + abort(); + } + + eventloop->eventloop_ops->pollsetup(eventloop); + + mowgli_eventloop_synchronize(eventloop); + + return eventloop; +} + +void mowgli_eventloop_destroy(mowgli_eventloop_t *eventloop) +{ + eventloop->eventloop_ops->pollshutdown(eventloop); + + mowgli_mutex_uninit(&eventloop->mutex); + mowgli_heap_free(eventloop_heap, eventloop); +} + +void mowgli_eventloop_run(mowgli_eventloop_t *eventloop) +{ + return_if_fail(eventloop != NULL); + + mowgli_mutex_lock(&eventloop->mutex); + + eventloop->death_requested = false; + + while (!eventloop->death_requested) + eventloop->eventloop_ops->run_once(eventloop); + + mowgli_mutex_unlock(&eventloop->mutex); +} + +void mowgli_eventloop_run_once(mowgli_eventloop_t *eventloop) +{ + return_if_fail(eventloop != NULL); + + mowgli_mutex_lock(&eventloop->mutex); + + eventloop->eventloop_ops->run_once(eventloop); + + mowgli_mutex_unlock(&eventloop->mutex); +} + +void mowgli_eventloop_timeout_once(mowgli_eventloop_t *eventloop, int timeout) +{ + return_if_fail(eventloop != NULL); + + mowgli_mutex_lock(&eventloop->mutex); + + if (timeout >= 0) + eventloop->eventloop_ops->timeout_once(eventloop, timeout); + else + eventloop->eventloop_ops->run_once(eventloop); + + mowgli_mutex_unlock(&eventloop->mutex); +} + +void mowgli_eventloop_break(mowgli_eventloop_t *eventloop) +{ + return_if_fail(eventloop != NULL); + + eventloop->death_requested = true; +} + +/* convenience function to request null pollops */ +void mowgli_eventloop_timers_only(mowgli_eventloop_t *eventloop) +{ + return_if_fail(eventloop != NULL); + + eventloop->eventloop_ops = &_mowgli_null_pollops; +} + +/* userdata setting/getting functions (for bindings) */ +void *mowgli_eventloop_get_data(mowgli_eventloop_t *eventloop) +{ + return_val_if_fail(eventloop != NULL, NULL); + + return eventloop->data; +} + +void mowgli_eventloop_set_data(mowgli_eventloop_t *eventloop, void *data) +{ + return_if_fail(eventloop != NULL); + + eventloop->data = data; +} diff --git a/src/libmowgli/eventloop/eventloop.h b/src/libmowgli/eventloop/eventloop.h new file mode 100644 index 0000000..4c07b27 --- /dev/null +++ b/src/libmowgli/eventloop/eventloop.h @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2011, 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __MOWGLI_EVENTLOOP_EVENTLOOP_H__ +#define __MOWGLI_EVENTLOOP_EVENTLOOP_H__ + +#ifndef _WIN32 + +typedef int mowgli_descriptor_t; + +#else + +typedef SOCKET mowgli_descriptor_t; + +#endif + +typedef enum { + MOWGLI_EVENTLOOP_TYPE_POLLABLE, + MOWGLI_EVENTLOOP_TYPE_HELPER, + MOWGLI_EVENTLOOP_TYPE_ERROR = -1 +} mowgli_eventloop_io_type_t; + +typedef struct { + mowgli_eventloop_io_type_t type; +} mowgli_eventloop_io_obj_t; + +typedef struct _mowgli_eventloop mowgli_eventloop_t; + +typedef struct _mowgli_pollable mowgli_eventloop_pollable_t; +typedef struct _mowgli_helper mowgli_eventloop_helper_proc_t; + +typedef struct _mowgli_linebuf mowgli_linebuf_t; + +typedef enum { + MOWGLI_EVENTLOOP_IO_READ, + MOWGLI_EVENTLOOP_IO_WRITE, + MOWGLI_EVENTLOOP_IO_ERROR = -1 +} mowgli_eventloop_io_dir_t; + +typedef void mowgli_eventloop_io_t; + +/* checked casts */ +static inline mowgli_eventloop_pollable_t *mowgli_eventloop_io_pollable(mowgli_eventloop_io_t *io) +{ + mowgli_eventloop_io_obj_t *obj = (mowgli_eventloop_io_obj_t *) io; + + return_val_if_fail(io != NULL, NULL); + return_val_if_fail(obj->type == MOWGLI_EVENTLOOP_TYPE_POLLABLE, NULL); + + return (mowgli_eventloop_pollable_t *) io; +} + +static inline mowgli_eventloop_helper_proc_t *mowgli_eventloop_io_helper(mowgli_eventloop_io_t *io) +{ + mowgli_eventloop_io_obj_t *obj = (mowgli_eventloop_io_obj_t *) io; + + return_val_if_fail(io != NULL, NULL); + return_val_if_fail(obj->type == MOWGLI_EVENTLOOP_TYPE_HELPER, NULL); + + return (mowgli_eventloop_helper_proc_t *) io; +} + +static inline mowgli_eventloop_io_type_t mowgli_eventloop_io_type(mowgli_eventloop_io_t *io) +{ + mowgli_eventloop_io_obj_t *obj = (mowgli_eventloop_io_obj_t *) io; + + return_val_if_fail(io != NULL, MOWGLI_EVENTLOOP_TYPE_ERROR); + + return obj->type; +} + +typedef void mowgli_eventloop_io_cb_t(mowgli_eventloop_t *eventloop, mowgli_eventloop_io_t *io, mowgli_eventloop_io_dir_t dir, void *userdata); + +struct _mowgli_pollable { + mowgli_eventloop_io_obj_t type; + + mowgli_descriptor_t fd; + unsigned int slot; + unsigned int events; + + mowgli_eventloop_io_cb_t *read_function; + mowgli_eventloop_io_cb_t *write_function; + mowgli_eventloop_io_cb_t *error_function; + + void *userdata; + + mowgli_node_t node; + + mowgli_eventloop_t *eventloop; +}; + +typedef struct { + void (*timeout_once)(mowgli_eventloop_t *eventloop, int timeout); + void (*run_once)(mowgli_eventloop_t *eventloop); + void (*pollsetup)(mowgli_eventloop_t *eventloop); + void (*pollshutdown)(mowgli_eventloop_t *eventloop); + void (*setselect)(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function); + void (*select)(mowgli_eventloop_t *eventloop, int time); + void (*destroy)(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable); +} mowgli_eventloop_ops_t; + +struct _mowgli_eventloop { + time_t currtime; + time_t deadline; + + const char *last_ran; + + mowgli_list_t timer_list; + mowgli_mutex_t mutex; + + mowgli_eventloop_ops_t *eventloop_ops; + void *poller; + + bool death_requested; + + void *data; +}; + +typedef void mowgli_event_dispatch_func_t(void *userdata); + +typedef struct { + mowgli_node_t node; + + mowgli_event_dispatch_func_t *func; + void *arg; + const char *name; + time_t frequency; + time_t when; + bool active; +} mowgli_eventloop_timer_t; + +static inline void mowgli_eventloop_set_time(mowgli_eventloop_t *eventloop, time_t newtime) +{ + return_if_fail(eventloop != NULL); + + eventloop->currtime = newtime; +} + +static inline time_t mowgli_eventloop_get_time(mowgli_eventloop_t *eventloop) +{ + return_val_if_fail(eventloop != NULL, 0); + + return eventloop->currtime; +} + +static inline void mowgli_eventloop_synchronize(mowgli_eventloop_t *eventloop) +{ + mowgli_eventloop_set_time(eventloop, time(NULL)); +} + +static inline bool mowgli_eventloop_ignore_errno(int error) +{ + switch (error) + { +#ifdef EINPROGRESS + case EINPROGRESS: +#endif +#if defined(EWOULDBLOCK) + case EWOULDBLOCK: +#endif +#if defined(EAGAIN) && (EWOULDBLOCK != EAGAIN) + case EAGAIN: +#endif +#ifdef EINTR + case EINTR: +#endif +#ifdef ERESTART + case ERESTART: +#endif +#ifdef ENOBUFS + case ENOBUFS: +#endif +#ifdef ENOENT + case ENOENT: +#endif + return true; + default: + break; + } + + return false; +} + +typedef void mowgli_eventloop_helper_start_fn_t(mowgli_eventloop_helper_proc_t *helper, void *userdata); + +struct _mowgli_helper { + mowgli_eventloop_io_obj_t type; + + mowgli_process_t *child; + mowgli_eventloop_t *eventloop; + + mowgli_descriptor_t fd; + mowgli_eventloop_pollable_t *pfd; + + mowgli_eventloop_io_cb_t *read_function; + + void *userdata; +}; + +/* helper.c */ +extern mowgli_eventloop_helper_proc_t *mowgli_helper_create(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_start_fn_t *start_fn, const char *helpername, void *userdata); + +/* creation of helpers inside other executable images */ +extern mowgli_eventloop_helper_proc_t *mowgli_helper_spawn(mowgli_eventloop_t *eventloop, const char *path, char *const argv[]); +extern mowgli_eventloop_helper_proc_t *mowgli_helper_setup(mowgli_eventloop_t *eventloop); + +/* synchronization of helpers happens on reading from mowgli_eventloop_helper_proc_t::in_pfd. */ +extern void mowgli_helper_set_read_cb(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper, mowgli_eventloop_io_cb_t *read_fn); +extern void mowgli_helper_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper); + +/* null_pollops.c */ +extern void mowgli_simple_eventloop_run_once(mowgli_eventloop_t *eventloop); +extern void mowgli_simple_eventloop_timeout_once(mowgli_eventloop_t *eventloop, int timeout); +extern void mowgli_simple_eventloop_error_handler(mowgli_eventloop_t *eventloop, mowgli_eventloop_io_t *io, mowgli_eventloop_io_dir_t dir, void *userdata); + +/* eventloop.c */ +extern mowgli_eventloop_t *mowgli_eventloop_create(void); +extern void mowgli_eventloop_destroy(mowgli_eventloop_t *eventloop); +extern void mowgli_eventloop_run(mowgli_eventloop_t *eventloop); +extern void mowgli_eventloop_run_once(mowgli_eventloop_t *eventloop); +extern void mowgli_eventloop_timeout_once(mowgli_eventloop_t *eventloop, int timeout); +extern void mowgli_eventloop_break(mowgli_eventloop_t *eventloop); +extern void mowgli_eventloop_timers_only(mowgli_eventloop_t *eventloop); +extern void mowgli_eventloop_set_data(mowgli_eventloop_t *eventloop, void *data); +extern void *mowgli_eventloop_get_data(mowgli_eventloop_t *eventloop); + +/* timer.c */ +extern mowgli_eventloop_timer_t *mowgli_timer_add(mowgli_eventloop_t *eventloop, const char *name, mowgli_event_dispatch_func_t *func, void *arg, time_t when); +extern mowgli_eventloop_timer_t *mowgli_timer_add_once(mowgli_eventloop_t *eventloop, const char *name, mowgli_event_dispatch_func_t *func, void *arg, time_t when); +extern void mowgli_timer_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_timer_t *timer); +extern void mowgli_eventloop_run_timers(mowgli_eventloop_t *eventloop); +extern time_t mowgli_eventloop_next_timer(mowgli_eventloop_t *eventloop); +extern mowgli_eventloop_timer_t *mowgli_timer_find(mowgli_eventloop_t *eventloop, mowgli_event_dispatch_func_t *func, void *arg); + +/* pollable.c */ +extern mowgli_eventloop_pollable_t *mowgli_pollable_create(mowgli_eventloop_t *eventloop, mowgli_descriptor_t fd, void *userdata); +extern void mowgli_pollable_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable); +extern void mowgli_pollable_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function); +extern void mowgli_pollable_set_nonblocking(mowgli_eventloop_pollable_t *pollable, bool nonblocking); + +#endif + diff --git a/src/libmowgli/eventloop/helper.c b/src/libmowgli/eventloop/helper.c new file mode 100644 index 0000000..b89e447 --- /dev/null +++ b/src/libmowgli/eventloop/helper.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +typedef struct { + mowgli_eventloop_helper_start_fn_t *start_fn; + void *userdata; + mowgli_descriptor_t fd; +} mowgli_helper_create_req_t; + +static void +mowgli_helper_trampoline(mowgli_helper_create_req_t *req) +{ + mowgli_eventloop_helper_proc_t *helper; +#ifndef _WIN32 + int i, x; +#endif + + return_if_fail(req != NULL); + return_if_fail(req->start_fn != NULL); + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->fd = req->fd; + +#ifndef _WIN32 + for (i = 0; i < 1024; i++) + { + if (i != req->fd) + close(i); + } + + x = open("/dev/null", O_RDWR); + + for (i = 0; i < 2; i++) + { + if (req->fd != i) + dup2(x, i); + } + + if (x > 2) + close(x); +#endif + + helper->eventloop = mowgli_eventloop_create(); + helper->pfd = mowgli_pollable_create(helper->eventloop, helper->fd, helper); + helper->userdata = req->userdata; + + mowgli_pollable_set_nonblocking(helper->pfd, true); + + req->start_fn(helper, helper->userdata); +} + +mowgli_eventloop_helper_proc_t * +mowgli_helper_create(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_start_fn_t *start_fn, const char *helpername, void *userdata) +{ + mowgli_eventloop_helper_proc_t *helper; + mowgli_helper_create_req_t child; + int io_fd[2]; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(start_fn != NULL, NULL); + + child.start_fn = start_fn; + child.userdata = userdata; + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + + socketpair(AF_UNIX, SOCK_STREAM, 0, io_fd); + + /* set up helper/child fd mapping */ + helper->fd = io_fd[0]; + child.fd = io_fd[1]; + + /* make pollables and make them non-blocking */ + helper->pfd = mowgli_pollable_create(eventloop, helper->fd, helper); + mowgli_pollable_set_nonblocking(helper->pfd, true); + + /* spawn helper process using mowgli_process_clone() */ + helper->child = mowgli_process_clone((mowgli_process_start_fn_t) mowgli_helper_trampoline, helpername, &child); + + if (helper->child == NULL) + { + mowgli_pollable_destroy(eventloop, helper->pfd); + + close(io_fd[0]); + close(io_fd[1]); + + mowgli_free(helper); + return NULL; + } + + close(child.fd); + + return helper; +} + +mowgli_eventloop_helper_proc_t * +mowgli_helper_spawn(mowgli_eventloop_t *eventloop, const char *path, char *const argv[]) +{ + mowgli_eventloop_helper_proc_t *helper; + int io_fd[2]; + char buf[64]; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(path != NULL, NULL); + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + + socketpair(AF_UNIX, SOCK_STREAM, 0, io_fd); + + /* set up helper/child fd mapping */ + helper->fd = io_fd[0]; + + /* make pollables and make them non-blocking */ + helper->pfd = mowgli_pollable_create(eventloop, helper->fd, helper); + + snprintf(buf, sizeof buf, "%d", io_fd[1]); + setenv("IO_FD", buf, 1); + + /* Spawn helper process using mowgli_process_spawn(), helper will get + * IO_FD mapping from getenv(). Ugly hack, but it works... + * --nenolod + */ + helper->child = mowgli_process_spawn(path, argv); + + if (helper->child == NULL) + { + mowgli_pollable_destroy(eventloop, helper->pfd); + + close(io_fd[0]); + close(io_fd[1]); + + mowgli_free(helper); + return NULL; + } + + close(io_fd[1]); + + return helper; +} + +/* Set up a helper connection to parent using getenv() */ +mowgli_eventloop_helper_proc_t * +mowgli_helper_setup(mowgli_eventloop_t *eventloop) +{ + mowgli_eventloop_helper_proc_t *helper; + const char *env_io_fd; + + env_io_fd = getenv("IO_FD"); + + /* this shouldn't be a hard-fail because some idiot may run the helper from + * the cmdline. allow the helper to error out gracefully if not spawned as + * a helper. + */ + if (env_io_fd == NULL) + return NULL; + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + helper->fd = atoi(env_io_fd); + helper->pfd = mowgli_pollable_create(helper->eventloop, helper->fd, helper); + + mowgli_pollable_set_nonblocking(helper->pfd, true); + + return helper; +} + +static void +mowgli_helper_io_trampoline(mowgli_eventloop_t *eventloop, mowgli_eventloop_io_t *io, mowgli_eventloop_io_dir_t dir, void *userdata) +{ + mowgli_eventloop_helper_proc_t *helper = userdata; + + switch (dir) { + case MOWGLI_EVENTLOOP_IO_READ: + if (helper->read_function != NULL) + return helper->read_function(eventloop, helper, MOWGLI_EVENTLOOP_IO_READ, helper->userdata); + default: + break; + } +} + +void +mowgli_helper_set_read_cb(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper, mowgli_eventloop_io_cb_t *read_fn) +{ + return_if_fail(eventloop != NULL); + return_if_fail(helper != NULL); + + if (read_fn == NULL) + mowgli_pollable_setselect(eventloop, helper->pfd, MOWGLI_EVENTLOOP_IO_READ, NULL); + + helper->read_function = read_fn; + mowgli_pollable_setselect(eventloop, helper->pfd, MOWGLI_EVENTLOOP_IO_READ, mowgli_helper_io_trampoline); +} + +void +mowgli_helper_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper) +{ + return_if_fail(eventloop != NULL); + return_if_fail(helper != NULL); + + mowgli_process_kill(helper->child); + mowgli_pollable_destroy(eventloop, helper->pfd); + close(helper->fd); + + mowgli_free(helper); +} diff --git a/src/libmowgli/eventloop/kqueue_pollops.c b/src/libmowgli/eventloop/kqueue_pollops.c new file mode 100644 index 0000000..7bfa2bb --- /dev/null +++ b/src/libmowgli/eventloop/kqueue_pollops.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * Copyright (c) 2011 Jilles Tjoelker <jilles@stack.nl>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_KQUEUE + +#include <sys/event.h> + +typedef struct { + int kqueue_fd; + int nevents; + struct kevent *events; +} mowgli_kqueue_eventloop_private_t; + +static void mowgli_kqueue_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_kqueue_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_kqueue_eventloop_private_t)); + eventloop->poller = priv; + + priv->nevents = getdtablesize(); + priv->kqueue_fd = kqueue(); + priv->events = mowgli_alloc(sizeof(struct kevent) * priv->nevents); + + return; +} + +static void mowgli_kqueue_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_kqueue_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + close(priv->kqueue_fd); + + mowgli_free(priv->events); + mowgli_free(priv); + return; +} + +static void mowgli_kqueue_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_kqueue_eventloop_private_t *priv; + struct kevent event; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + + EV_SET(&event, pollable->fd, EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, pollable); + if (kevent(priv->kqueue_fd, &event, 1, NULL, 0, + &(const struct timespec){ .tv_sec = 0, .tv_nsec = 0} + ) != 0) + { + mowgli_log("mowgli_kqueue_eventloop_setselect(): kevent failed: %d (%s)", errno, strerror(errno)); + } +} + +static void mowgli_kqueue_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_kqueue_eventloop_private_t *priv; + mowgli_eventloop_io_cb_t **fptr; + struct kevent event; + int filter; + bool change; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + change = false; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + fptr = &pollable->read_function; + filter = EVFILT_READ; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + fptr = &pollable->write_function; + filter = EVFILT_WRITE; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + return; + } + + change = (*fptr != NULL) != (event_function != NULL); + + *fptr = event_function; + + if (!change) + return; + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + EV_SET(&event, pollable->fd, filter, + event_function ? EV_ADD : EV_DELETE, 0, 0, pollable); + if (kevent(priv->kqueue_fd, &event, 1, NULL, 0, + &(const struct timespec){ .tv_sec = 0, .tv_nsec = 0} + ) != 0) + { + mowgli_log("mowgli_kqueue_eventloop_setselect(): kevent failed: %d (%s)", errno, strerror(errno)); + } +} + +static void mowgli_kqueue_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + mowgli_kqueue_eventloop_private_t *priv; + int i, num, o_errno; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + num = kevent(priv->kqueue_fd, NULL, 0, priv->events, priv->nevents, + delay >= 0 ? &(const struct timespec){ .tv_sec = delay / 1000, + .tv_nsec = delay % 1000 * 1000000 } : NULL); + + o_errno = errno; + mowgli_eventloop_synchronize(eventloop); + + if (num < 0) + { + if (mowgli_eventloop_ignore_errno(o_errno)) + return; + + mowgli_log("mowgli_kqueue_eventloop_select(): kevent failed: %d (%s)", o_errno, strerror(o_errno)); + return; + } + + for (i = 0; i < num; i++) + { + mowgli_eventloop_pollable_t *pollable = priv->events[i].udata; + + if (priv->events[i].filter == EVFILT_READ && + pollable->read_function != NULL) + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + + if (priv->events[i].filter == EVFILT_WRITE && + pollable->write_function != NULL) + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + + /* XXX Perhaps we need to recheck read_function and + * write_function now. + */ + } +} + +mowgli_eventloop_ops_t _mowgli_kqueue_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_kqueue_eventloop_pollsetup, + .pollshutdown = mowgli_kqueue_eventloop_pollshutdown, + .setselect = mowgli_kqueue_eventloop_setselect, + .select = mowgli_kqueue_eventloop_select, + .destroy = mowgli_kqueue_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/null_pollops.c b/src/libmowgli/eventloop/null_pollops.c new file mode 100644 index 0000000..8fcabe4 --- /dev/null +++ b/src/libmowgli/eventloop/null_pollops.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +void mowgli_simple_eventloop_timeout_once(mowgli_eventloop_t *eventloop, int timeout) +{ + time_t delay, currtime; + int t; + + return_if_fail(eventloop != NULL); + return_if_fail(eventloop->eventloop_ops != NULL); + + mowgli_eventloop_synchronize(eventloop); + + currtime = mowgli_eventloop_get_time(eventloop); + delay = mowgli_eventloop_next_timer(eventloop); + + if (delay <= currtime) + { + mowgli_eventloop_run_timers(eventloop); + mowgli_eventloop_synchronize(eventloop); + + currtime = mowgli_eventloop_get_time(eventloop); + delay = mowgli_eventloop_next_timer(eventloop); + } + + if (timeout) + t = timeout; + else + { + if (delay <= currtime) + t = -1; + else + t = (delay - currtime) * 1000; + } + +#ifdef DEBUG + mowgli_log("delay: %ld, currtime: %ld, select period: %d", delay, currtime, t); +#endif + + eventloop->eventloop_ops->select(eventloop, t); + +} + +void mowgli_simple_eventloop_run_once(mowgli_eventloop_t *eventloop) +{ + eventloop->eventloop_ops->timeout_once(eventloop, 0); +} + +void mowgli_simple_eventloop_error_handler(mowgli_eventloop_t *eventloop, mowgli_eventloop_io_t *io, mowgli_eventloop_io_dir_t dir, void *userdata) +{ + mowgli_eventloop_pollable_t *pollable = mowgli_eventloop_io_pollable(io); + + if (pollable != NULL) + mowgli_pollable_destroy(eventloop, pollable); +} + +static void mowgli_null_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + return; +} + +static void mowgli_null_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + return; +} + +static void mowgli_null_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + return; +} + +static void mowgli_null_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_log("null eventloop does not really do polling, events for pollable<%p> will be ignored", pollable); + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + + return; +} + +static void mowgli_null_eventloop_select(mowgli_eventloop_t *eventloop, int time) +{ + usleep(time); +} + +mowgli_eventloop_ops_t _mowgli_null_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_null_eventloop_pollsetup, + .pollshutdown = mowgli_null_eventloop_pollshutdown, + .setselect = mowgli_null_eventloop_setselect, + .select = mowgli_null_eventloop_select, + .destroy = mowgli_null_eventloop_destroy, +}; diff --git a/src/libmowgli/eventloop/poll_pollops.c b/src/libmowgli/eventloop/poll_pollops.c new file mode 100644 index 0000000..0bea454 --- /dev/null +++ b/src/libmowgli/eventloop/poll_pollops.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_POLL_H + +#include <poll.h> + +#ifndef POLLRDNORM +#define POLLRDNORM POLLIN +#endif +#ifndef POLLWRNORM +#define POLLWRNORM POLLOUT +#endif + +typedef struct { + struct pollfd pollfds[FD_SETSIZE]; + nfds_t nfds; + mowgli_list_t pollable_list; +} mowgli_poll_eventloop_private_t; + +static nfds_t update_poll_fds(mowgli_eventloop_t *eventloop) +{ + mowgli_node_t *n, *tn; + mowgli_poll_eventloop_private_t *priv; + nfds_t slot = 0; + + return_val_if_fail(eventloop != NULL, 0); + + priv = eventloop->poller; + + memset(priv->pollfds, '\0', sizeof(priv->pollfds)); + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + mowgli_eventloop_pollable_t *pollable = n->data; + +#ifdef DEBUG + mowgli_log("considering fd %d pollable %p count %d", pollable->fd, pollable, priv->pollable_list.count); +#endif + + if (pollable->read_function || pollable->write_function) + { + priv->pollfds[slot].fd = pollable->fd; + + if (pollable->read_function) + priv->pollfds[slot].events |= POLLRDNORM; + + if (pollable->write_function) + priv->pollfds[slot].events |= POLLWRNORM; + + priv->pollfds[slot].revents = 0; + pollable->slot = slot; + slot++; + } + else + pollable->slot = -1; + } + + return slot; +} + +static void mowgli_poll_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_poll_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_poll_eventloop_private_t)); + eventloop->poller = priv; + + return; +} + +static void mowgli_poll_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_node_t *n, *tn; + mowgli_poll_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + mowgli_node_delete(n, &priv->pollable_list); + } + + mowgli_free(priv); + return; +} + +static void mowgli_poll_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_poll_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + + mowgli_node_delete(&pollable->node, &priv->pollable_list); +} + +static void mowgli_poll_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_poll_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + if (pollable->read_function || pollable->write_function) + mowgli_node_delete(&pollable->node, &priv->pollable_list); + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function || pollable->write_function) + mowgli_node_add(pollable, &pollable->node, &priv->pollable_list); + + return; +} + +static void mowgli_poll_eventloop_select(mowgli_eventloop_t *eventloop, int time) +{ + mowgli_node_t *n, *tn; + nfds_t nfds; + mowgli_eventloop_pollable_t *pollable; + mowgli_poll_eventloop_private_t *priv; + int slot; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + nfds = update_poll_fds(eventloop); + + if (poll(priv->pollfds, nfds, time) > 0) + { + mowgli_eventloop_synchronize(eventloop); + + /* iterate twice so we don't touch freed memory if a pollable is destroyed */ + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + pollable = n->data; + slot = pollable->slot; + + if (slot == -1 || priv->pollfds[slot].revents == 0) + continue; + + if (priv->pollfds[slot].revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR) && pollable->read_function) + { +#ifdef DEBUG + mowgli_log("run %p(%p, %p, MOWGLI_EVENTLOOP_IO_READ, %p)\n", pollable->read_function, eventloop, pollable, pollable->userdata); +#endif + + priv->pollfds[slot].events &= ~(POLLRDNORM | POLLIN); + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + } + } + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + pollable = n->data; + slot = pollable->slot; + + if (slot == -1 || priv->pollfds[slot].revents == 0) + continue; + + if (priv->pollfds[slot].revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR) && pollable->write_function) + { +#ifdef DEBUG + mowgli_log("run %p(%p, %p, MOWGLI_EVENTLOOP_IO_WRITE, %p)\n", pollable->write_function, eventloop, pollable, pollable->userdata); +#endif + + priv->pollfds[slot].events &= ~(POLLWRNORM | POLLOUT); + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + } + } + } +} + +mowgli_eventloop_ops_t _mowgli_poll_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_poll_eventloop_pollsetup, + .pollshutdown = mowgli_poll_eventloop_pollshutdown, + .setselect = mowgli_poll_eventloop_setselect, + .select = mowgli_poll_eventloop_select, + .destroy = mowgli_poll_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/pollable.c b/src/libmowgli/eventloop/pollable.c new file mode 100644 index 0000000..e2c2e2a --- /dev/null +++ b/src/libmowgli/eventloop/pollable.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +static mowgli_heap_t *pollable_heap = NULL; + +mowgli_eventloop_pollable_t *mowgli_pollable_create(mowgli_eventloop_t *eventloop, mowgli_descriptor_t fd, void *userdata) +{ + mowgli_eventloop_pollable_t *pollable; + + return_val_if_fail(eventloop != NULL, NULL); + + if (pollable_heap == NULL) + pollable_heap = mowgli_heap_create(sizeof(mowgli_eventloop_pollable_t), 16, BH_NOW); + + pollable = mowgli_heap_alloc(pollable_heap); + + pollable->eventloop = eventloop; + pollable->type.type = MOWGLI_EVENTLOOP_TYPE_POLLABLE; + pollable->fd = fd; + pollable->userdata = userdata; + + return pollable; +} + +void mowgli_pollable_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + /* unregister any interest in the pollable. */ + eventloop->eventloop_ops->destroy(eventloop, pollable); + + mowgli_heap_free(pollable_heap, pollable); +} + +void mowgli_pollable_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + return_if_fail(eventloop->eventloop_ops != NULL); + + eventloop->eventloop_ops->setselect(eventloop, pollable, dir, event_function); +} + +void mowgli_pollable_set_nonblocking(mowgli_eventloop_pollable_t *pollable, bool nonblocking) +{ +#if defined(HAVE_FCNTL) + unsigned long flags; + + return_if_fail(pollable != NULL); + + flags = fcntl(pollable->fd, F_GETFL); + + if (nonblocking) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + fcntl(pollable->fd, F_SETFL, flags); +#elif defined(HAVE_WINSOCK2_H) + u_long mode; + + return_if_fail(pollable != NULL); + + mode = nonblocking; + + ioctlsocket(pollable->fd, FIONBIO, &mode); +#endif +} diff --git a/src/libmowgli/eventloop/ports_pollops.c b/src/libmowgli/eventloop/ports_pollops.c new file mode 100644 index 0000000..bc838da --- /dev/null +++ b/src/libmowgli/eventloop/ports_pollops.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_PORT_CREATE + +#include <port.h> + +typedef struct { + int port_fd; + int pfd_size; + port_event_t *pfd; +} mowgli_ports_eventloop_private_t; + +static void mowgli_ports_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_ports_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_ports_eventloop_private_t)); + eventloop->poller = priv; + + priv->pfd_size = getdtablesize(); + priv->port_fd = port_create(); + priv->pfd = mowgli_alloc(sizeof(port_event_t) * priv->pfd_size); + + return; +} + +static void mowgli_ports_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_ports_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + close(priv->port_fd); + + mowgli_free(priv->pfd); + mowgli_free(priv); + return; +} + +static void mowgli_ports_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_ports_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + pollable->slot = 0; + + if (port_dissociate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd) < 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_ports_eventloop_destroy(): port_dissociate failed: %d (%s)", errno, strerror(errno)); + } +} + +static void mowgli_ports_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_ports_eventloop_private_t *priv; + unsigned int old_flags; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + old_flags = pollable->slot; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + pollable->slot |= POLLIN; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + pollable->slot |= POLLOUT; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function == NULL) + pollable->slot &= ~POLLIN; + + if (pollable->write_function == NULL) + pollable->slot &= ~POLLOUT; + + if (old_flags == 0 && pollable->slot == 0) + return; + else if (pollable->slot == 0) + { + port_dissociate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd); + return; + } + + if (port_associate(priv->port_fd, PORT_SOURCE_FD, (uintptr_t) pollable->fd, pollable->slot, pollable) < 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_ports_eventloop_setselect(): port_associate failed: %d (%s)", errno, strerror(errno)); + } + + return; +} + +static void mowgli_ports_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + mowgli_ports_eventloop_private_t *priv; + int i, num, o_errno, nget = 1; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + num = port_getn(priv->port_fd, priv->pfd, priv->pfd_size, &nget, + delay >= 0 ? &(struct timespec){ .tv_sec = delay / 1000, .tv_nsec = delay % 1000 * 1000000 } : NULL); + + o_errno = errno; + mowgli_eventloop_synchronize(eventloop); + + if (num < 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_ports_eventloop_select(): port_getn failed: %d (%s)", o_errno, strerror(o_errno)); + return; + } + + for (i = 0; i < nget; i++) + { + mowgli_eventloop_pollable_t *pollable = priv->pfd[i].portev_user; + + if (priv->pfd[i].portev_events & (POLLIN | POLLHUP | POLLERR) && pollable->read_function != NULL) + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + + if (priv->pfd[i].portev_events & (POLLOUT | POLLHUP | POLLERR) && pollable->write_function != NULL) + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + } +} + +mowgli_eventloop_ops_t _mowgli_ports_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_ports_eventloop_pollsetup, + .pollshutdown = mowgli_ports_eventloop_pollshutdown, + .setselect = mowgli_ports_eventloop_setselect, + .select = mowgli_ports_eventloop_select, + .destroy = mowgli_ports_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/qnx_pollops.c b/src/libmowgli/eventloop/qnx_pollops.c new file mode 100644 index 0000000..7417ef4 --- /dev/null +++ b/src/libmowgli/eventloop/qnx_pollops.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_DISPATCH_BLOCK + +#include <sys/iofunc.h> +#include <sys/dispatch.h> + +typedef struct { + dispatch_t *dpp; + dispatch_context_t *ctp; +} mowgli_qnx_eventloop_private_t; + +static void mowgli_qnx_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_qnx_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_qnx_eventloop_private_t)); + eventloop->poller = priv; + + priv->dpp = dispatch_create(); + + return; +} + +static void mowgli_qnx_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_qnx_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + if (priv->ctp != NULL) + dispatch_context_free(priv->ctp); + + dispatch_destroy(priv->dpp); + mowgli_free(priv); + + return; +} + +static void mowgli_qnx_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_qnx_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + + if (select_detach(priv->dpp, pollable->fd) != 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_qnx_eventloop_destroy(): select_detach failed: %d (%s)", errno, strerror(errno)); + } +} + +static void mowgli_qnx_eventloop_event_cb(select_context_t *ctp, mowgli_descriptor_t fd, unsigned int flags, void *userdata) +{ + mowgli_eventloop_t *eventloop; + mowgli_eventloop_pollable_t *pollable; + + return_if_fail(ctp != NULL); + return_if_fail(userdata != NULL); + + pollable = userdata; + eventloop = pollable->eventloop; + + return_if_fail(eventloop != NULL); + + if (flags & (SELECT_FLAG_READ | SELECT_FLAG_EXCEPT)) + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + + if (flags & (SELECT_FLAG_WRITE | SELECT_FLAG_EXCEPT)) + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); +} + +static void mowgli_qnx_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_qnx_eventloop_private_t *priv; + select_attr_t attr = {}; + unsigned int old_flags; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + old_flags = pollable->slot; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + pollable->slot |= SELECT_FLAG_READ; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + pollable->slot |= SELECT_FLAG_WRITE; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function == NULL) + pollable->slot &= ~SELECT_FLAG_READ; + + if (pollable->write_function == NULL) + pollable->slot &= ~SELECT_FLAG_WRITE; + + if (old_flags == 0 && pollable->slot == 0) + return; + + if (old_flags) + select_detach(priv->dpp, pollable->fd); + + if (pollable->slot) + { + if (select_attach(priv->dpp, &attr, pollable->fd, pollable->slot, mowgli_qnx_eventloop_event_cb, pollable) != 0) + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_qnx_eventloop_setselect(): select_attach failed: %d (%s)", errno, strerror(errno)); + } + } + + return; +} + +static void mowgli_qnx_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + dispatch_context_t *new_ctp; + mowgli_qnx_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + /* set timeout if needed */ + dispatch_timeout(priv->dpp, delay >= 0 ? &(struct timespec){ .tv_sec = delay / 1000, .tv_nsec = delay % 1000 * 1000000 } : NULL); + + if (priv->ctp != NULL) + priv->ctp = dispatch_context_alloc(priv->dpp); + + /* if dispatch_block returns non-NULL, priv->ctp may have been realloc()'d, NULL is error condition */ + if ((new_ctp = dispatch_block(priv->ctp)) != NULL) + priv->ctp = new_ctp; + else + { + if (mowgli_eventloop_ignore_errno(errno)) + return; + + mowgli_log("mowgli_qnx_eventloop_select(): dispatch_block failed: %d (%s)", errno, strerror(errno)); + } + + mowgli_eventloop_synchronize(eventloop); +} + +mowgli_eventloop_ops_t _mowgli_qnx_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_qnx_eventloop_pollsetup, + .pollshutdown = mowgli_qnx_eventloop_pollshutdown, + .setselect = mowgli_qnx_eventloop_setselect, + .select = mowgli_qnx_eventloop_select, + .destroy = mowgli_qnx_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/select_pollops.c b/src/libmowgli/eventloop/select_pollops.c new file mode 100644 index 0000000..467b4f5 --- /dev/null +++ b/src/libmowgli/eventloop/select_pollops.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef HAVE_SELECT + +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif + +typedef struct { + mowgli_list_t pollable_list; +} mowgli_select_eventloop_private_t; + +static void mowgli_select_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + mowgli_select_eventloop_private_t *priv; + + priv = mowgli_alloc(sizeof(mowgli_select_eventloop_private_t)); + eventloop->poller = priv; + + return; +} + +static void mowgli_select_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + mowgli_node_t *n, *tn; + mowgli_select_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + mowgli_node_delete(n, &priv->pollable_list); + } + + mowgli_free(priv); + return; +} + +static void mowgli_select_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_select_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + + mowgli_node_delete(&pollable->node, &priv->pollable_list); +} + +static void mowgli_select_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_select_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + if (pollable->read_function || pollable->write_function) + mowgli_node_delete(&pollable->node, &priv->pollable_list); + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function || pollable->write_function) + mowgli_node_add(pollable, &pollable->node, &priv->pollable_list); + + return; +} + +static void mowgli_select_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + mowgli_node_t *n, *tn; + mowgli_eventloop_pollable_t *pollable; + mowgli_select_eventloop_private_t *priv; + mowgli_descriptor_t highest_fd = 0; + fd_set rfds, wfds, efds; + struct timeval tv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + mowgli_eventloop_pollable_t *pollable = n->data; + +#ifdef DEBUG + mowgli_log("considering fd %d pollable %p count %d", pollable->fd, pollable, priv->pollable_list.count); +#endif + + if (pollable->read_function || pollable->write_function) + { + if (pollable->fd > highest_fd) + highest_fd = pollable->fd; + + if (pollable->read_function) + { + FD_SET(pollable->fd, &rfds); + FD_SET(pollable->fd, &efds); + } + + if (pollable->write_function) + { + FD_SET(pollable->fd, &wfds); + FD_SET(pollable->fd, &efds); + } + } + } + + tv.tv_sec = 1; + + if (select(highest_fd + 1, &rfds, &wfds, &efds, &tv) > 0) + { + mowgli_eventloop_synchronize(eventloop); + + /* iterate twice so we don't touch freed memory if a pollable is destroyed */ + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + pollable = n->data; + + if ((FD_ISSET(pollable->fd, &rfds) || FD_ISSET(pollable->fd, &efds)) && pollable->read_function) + { +#ifdef DEBUG + mowgli_log("run %p(%p, %p, MOWGLI_EVENTLOOP_IO_READ, %p)\n", pollable->read_function, eventloop, pollable, pollable->userdata); +#endif + + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + } + } + + MOWGLI_ITER_FOREACH_SAFE(n, tn, priv->pollable_list.head) + { + pollable = n->data; + + if ((FD_ISSET(pollable->fd, &wfds) || FD_ISSET(pollable->fd, &efds)) && pollable->write_function) + { +#ifdef DEBUG + mowgli_log("run %p(%p, %p, MOWGLI_EVENTLOOP_IO_WRITE, %p)\n", pollable->write_function, eventloop, pollable, pollable->userdata); +#endif + + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + } + } + } +} + +mowgli_eventloop_ops_t _mowgli_select_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_select_eventloop_pollsetup, + .pollshutdown = mowgli_select_eventloop_pollshutdown, + .setselect = mowgli_select_eventloop_setselect, + .select = mowgli_select_eventloop_select, + .destroy = mowgli_select_eventloop_destroy, +}; + +#endif diff --git a/src/libmowgli/eventloop/timer.c b/src/libmowgli/eventloop/timer.c new file mode 100644 index 0000000..d8c5061 --- /dev/null +++ b/src/libmowgli/eventloop/timer.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org> + * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org) + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +static mowgli_heap_t *timer_heap = NULL; + +static mowgli_eventloop_timer_t *mowgli_timer_add_real(mowgli_eventloop_t *eventloop, + const char *name, mowgli_event_dispatch_func_t *func, void *arg, time_t when, time_t frequency) +{ + mowgli_eventloop_timer_t *timer; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(func != NULL, NULL); + + if (timer_heap == NULL) + timer_heap = mowgli_heap_create(sizeof(mowgli_eventloop_timer_t), 16, BH_NOW); + + timer = mowgli_heap_alloc(timer_heap); + + timer->func = func; + timer->name = name; + timer->arg = arg; + timer->when = mowgli_eventloop_get_time(eventloop) + when; + timer->frequency = frequency; + timer->active = true; + + if (eventloop->deadline <= mowgli_eventloop_get_time(eventloop) || timer->when <= eventloop->deadline) + eventloop->deadline = timer->when; + + mowgli_node_add(timer, &timer->node, &eventloop->timer_list); + +#ifdef DEBUG + mowgli_log("[timer(%p) add when:%d active:%d] [eventloop deadline:%d]", timer, timer->when, timer->active, eventloop->deadline); +#endif + + return timer; +} + +/* add an event to the table to be continually ran */ +mowgli_eventloop_timer_t *mowgli_timer_add(mowgli_eventloop_t *eventloop, const char *name, mowgli_event_dispatch_func_t *func, void *arg, time_t when) +{ + return mowgli_timer_add_real(eventloop, name, func, arg, when, when); +} + +/* adds an event to the table to be ran only once */ +mowgli_eventloop_timer_t *mowgli_timer_add_once(mowgli_eventloop_t *eventloop, const char *name, mowgli_event_dispatch_func_t *func, void *arg, time_t when) +{ + return mowgli_timer_add_real(eventloop, name, func, arg, when, 0); +} + +/* delete an event from the table */ +void mowgli_timer_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_timer_t *timer) +{ + return_if_fail(eventloop != NULL); + return_if_fail(timer != NULL); + + if (eventloop->last_ran == timer->name) + eventloop->last_ran = "<removed>"; + + mowgli_node_delete(&timer->node, &eventloop->timer_list); + mowgli_heap_free(timer_heap, timer); +} + +/* checks all pending events */ +void mowgli_eventloop_run_timers(mowgli_eventloop_t *eventloop) +{ + mowgli_node_t *n, *tn; + time_t currtime; + + return_if_fail(eventloop != NULL); + + currtime = mowgli_eventloop_get_time(eventloop); + + MOWGLI_ITER_FOREACH_SAFE(n, tn, eventloop->timer_list.head) + { + mowgli_eventloop_timer_t *timer = n->data; + + if (timer->active && timer->when <= currtime) + { + /* now we call it */ + eventloop->last_ran = timer->name; + timer->func(timer->arg); + + /* invalidate eventloop sleep-until time */ + eventloop->deadline = -1; + + /* event is scheduled more than once */ + if (timer->frequency) + timer->when = currtime + timer->frequency; + else + { + /* XXX: yuck. find a better way to handle this. */ + eventloop->last_ran = "<onceonly>"; + + mowgli_timer_destroy(eventloop, timer); + } + } + } +} + +/* returns the time the next mowgli_timer_run() should happen */ +time_t mowgli_eventloop_next_timer(mowgli_eventloop_t *eventloop) +{ + mowgli_node_t *n; + + return_val_if_fail(eventloop != NULL, 0); + + if (eventloop->deadline == -1) + { + MOWGLI_ITER_FOREACH(n, eventloop->timer_list.head) + { + mowgli_eventloop_timer_t *timer = n->data; + + if (timer->active && (timer->when < eventloop->deadline || eventloop->deadline == -1)) + eventloop->deadline = timer->when; + +#ifdef DEBUG + mowgli_log("timer %p active:%d when:%ld deadline:%ld", timer, timer->active, timer->when, eventloop->deadline); +#endif + } + } + +#ifdef DEBUG + mowgli_log("eventloop deadline:%ld", eventloop->deadline); +#endif + + return eventloop->deadline; +} + +/* finds an event in the table */ +mowgli_eventloop_timer_t *mowgli_timer_find(mowgli_eventloop_t *eventloop, mowgli_event_dispatch_func_t *func, void *arg) +{ + mowgli_node_t *n; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(func != NULL, NULL); + + MOWGLI_ITER_FOREACH(n, eventloop->timer_list.head) + { + mowgli_eventloop_timer_t *timer = n->data; + + if (timer->func == func && timer->arg == arg) + return timer; + } + + return NULL; +} + +/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs + * vim:ts=8 + * vim:sw=8 + * vim:noexpandtab + */ diff --git a/src/libmowgli/eventloop/windows_pollops.c b/src/libmowgli/eventloop/windows_pollops.c new file mode 100644 index 0000000..f1c9c0f --- /dev/null +++ b/src/libmowgli/eventloop/windows_pollops.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "mowgli.h" + +#ifdef _WIN32 + +#define DEFAULT_SOCKETMAX (2048) + +typedef struct { + WSAEVENT *pfd; + unsigned short pfd_size; + unsigned short last_slot; + mowgli_eventloop_pollable_t **pollables; +} mowgli_winsock_eventloop_private_t; + +static WSADATA wsock_env; + +void mowgli_winsock_bootstrap(void) +{ + int r; + + r = WSAStartup((short) 0x202, &wsock_env); + if (r != 0) + { + printf("mowgli bootstrap failure (win32): %d\n", r); + exit(EXIT_FAILURE); + } + + if (!wsock_env.iMaxSockets) + wsock_env.iMaxSockets = DEFAULT_SOCKETMAX; + else + wsock_env.iMaxSockets -= (wsock_env.iMaxSockets % MAXIMUM_WAIT_OBJECTS); +} + +static void mowgli_winsock_eventloop_pollsetup(mowgli_eventloop_t *eventloop) +{ + unsigned short i; + mowgli_winsock_eventloop_private_t *priv; + + return_if_fail(wsock_env.iMaxSockets > 0); + + priv = mowgli_alloc(sizeof(mowgli_winsock_eventloop_private_t)); + eventloop->poller = priv; + + priv->pfd_size = wsock_env.iMaxSockets; + priv->pfd = mowgli_alloc(sizeof(WSAEVENT) * priv->pfd_size); + priv->pollables = mowgli_alloc(sizeof(mowgli_eventloop_pollable_t *) * priv->pfd_size); + + /* sanitize NT port handle values to a known-good default */ + for (i = 0; i < priv->pfd_size; i++) + { + priv->pfd[i] = INVALID_HANDLE_VALUE; + priv->pollables[i] = NULL; + } + + return; +} + +static unsigned short mowgli_winsock_eventloop_find_slot(mowgli_winsock_eventloop_private_t *priv) +{ + unsigned short i = 1; + + return_val_if_fail(priv != NULL, 0); + + if (priv->last_slot) + i = priv->last_slot; + + for (; i < priv->pfd_size; i++) + { + if (priv->pfd[i] == INVALID_HANDLE_VALUE) + { + priv->last_slot = i; + return i; + } + } + + /* miss, try from beginning. */ + for (i = 1; i < priv->pfd_size; i++) + { + if (priv->pfd[i] == INVALID_HANDLE_VALUE) + { + priv->last_slot = i; + return i; + } + } + + /* if this happens, we're boned... */ + mowgli_log("out of handles for eventloop %p, aborting\n", priv); + abort(); + + return 0; +} + +static void mowgli_winsock_eventloop_pollshutdown(mowgli_eventloop_t *eventloop) +{ + unsigned short i; + mowgli_winsock_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + for (i = 0; i < priv->pfd_size; i++) + { + WSACloseEvent(priv->pfd[i]); + priv->pfd[i] = INVALID_HANDLE_VALUE; + } + + mowgli_free(priv->pfd); + mowgli_free(priv); + + return; +} + +static void mowgli_winsock_eventloop_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable) +{ + mowgli_winsock_eventloop_private_t *priv; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + + if (pollable->slot) + { + WSAEventSelect(pollable->fd, priv->pfd[pollable->slot], 0); + WSACloseEvent(priv->pfd[pollable->slot]); + + priv->pfd[pollable->slot] = INVALID_HANDLE_VALUE; + priv->pollables[pollable->slot] = NULL; + } + + pollable->slot = 0; + pollable->events = 0; +} + +static void mowgli_winsock_eventloop_setselect(mowgli_eventloop_t *eventloop, mowgli_eventloop_pollable_t *pollable, mowgli_eventloop_io_dir_t dir, mowgli_eventloop_io_cb_t *event_function) +{ + mowgli_winsock_eventloop_private_t *priv; + unsigned int old_flags; + + return_if_fail(eventloop != NULL); + return_if_fail(pollable != NULL); + + priv = eventloop->poller; + old_flags = pollable->events; + +#ifdef DEBUG + mowgli_log("setselect %p fd %d func %p", pollable, pollable->fd, event_function); +#endif + + switch (dir) + { + case MOWGLI_EVENTLOOP_IO_READ: + pollable->read_function = event_function; + pollable->events |= (FD_READ | FD_CLOSE | FD_ACCEPT | FD_OOB); + break; + case MOWGLI_EVENTLOOP_IO_WRITE: + pollable->write_function = event_function; + pollable->events |= (FD_WRITE | FD_CONNECT | FD_CLOSE); + break; + default: + mowgli_log("unhandled pollable direction %d", dir); + break; + } + +#ifdef DEBUG + mowgli_log("%p -> read %p : write %p", pollable, pollable->read_function, pollable->write_function); +#endif + + if (pollable->read_function == NULL) + pollable->events &= ~(FD_READ | FD_CLOSE | FD_ACCEPT | FD_OOB); + + if (pollable->write_function == NULL) + pollable->events &= ~(FD_WRITE | FD_CONNECT | FD_CLOSE); + + if (old_flags == 0 && pollable->events == 0) + return; + else if (pollable->events <= 0) + { + mowgli_winsock_eventloop_destroy(eventloop, pollable); + return; + } + + /* set up the HANDLE if we have not already */ + if (!pollable->slot) + { + pollable->slot = mowgli_winsock_eventloop_find_slot(priv); + + priv->pfd[pollable->slot] = WSACreateEvent(); + priv->pollables[pollable->slot] = pollable; + } + + if (WSAEventSelect(pollable->fd, priv->pfd[pollable->slot], pollable->events) != 0) + { + if (mowgli_eventloop_ignore_errno(WSAGetLastError())) + return; + + mowgli_log("mowgli_winsock_eventloop_setselect(): WSAEventSelect failed: %d", WSAGetLastError()); + } + + return; +} + +static void mowgli_winsock_eventloop_select(mowgli_eventloop_t *eventloop, int delay) +{ + mowgli_winsock_eventloop_private_t *priv; + int i, j; + DWORD result; + + return_if_fail(eventloop != NULL); + + priv = eventloop->poller; + + return_if_fail(priv->pfd_size % MAXIMUM_WAIT_OBJECTS == 0); + + for (i = 0; i < priv->pfd_size; i += MAXIMUM_WAIT_OBJECTS) + { + result = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, priv->pfd + i, FALSE, delay); + + if (result == WAIT_FAILED) + { + if (mowgli_eventloop_ignore_errno(WSAGetLastError())) + return; + + mowgli_log("mowgli_winsock_eventloop_select(): WaitForMultipleObjects failed: %d", WSAGetLastError()); + return; + } + + for (j = (result - WAIT_OBJECT_0); j < MAXIMUM_WAIT_OBJECTS; j++) + { + mowgli_eventloop_pollable_t *pollable = priv->pollables[i + j]; + WSANETWORKEVENTS events; + + WSAEnumNetworkEvents(pollable->fd, priv->pfd[pollable->slot], &events); + + if (events.lNetworkEvents & (FD_READ | FD_CLOSE | FD_ACCEPT | FD_OOB) && pollable->read_function != NULL) + pollable->read_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_READ, pollable->userdata); + + if (events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE) && pollable->write_function != NULL) + pollable->write_function(eventloop, pollable, MOWGLI_EVENTLOOP_IO_WRITE, pollable->userdata); + } + } + + mowgli_eventloop_synchronize(eventloop); +} + +mowgli_eventloop_ops_t _mowgli_winsock_pollops = { + .timeout_once = mowgli_simple_eventloop_timeout_once, + .run_once = mowgli_simple_eventloop_run_once, + .pollsetup = mowgli_winsock_eventloop_pollsetup, + .pollshutdown = mowgli_winsock_eventloop_pollshutdown, + .setselect = mowgli_winsock_eventloop_setselect, + .select = mowgli_winsock_eventloop_select, + .destroy = mowgli_winsock_eventloop_destroy, +}; + +#endif |