summaryrefslogtreecommitdiff
path: root/src/libmowgli/eventloop
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmowgli/eventloop')
-rw-r--r--src/libmowgli/eventloop/Makefile14
-rw-r--r--src/libmowgli/eventloop/epoll_pollops.c198
-rw-r--r--src/libmowgli/eventloop/eventloop.c170
-rw-r--r--src/libmowgli/eventloop/eventloop.h259
-rw-r--r--src/libmowgli/eventloop/helper.c230
-rw-r--r--src/libmowgli/eventloop/kqueue_pollops.c189
-rw-r--r--src/libmowgli/eventloop/null_pollops.c124
-rw-r--r--src/libmowgli/eventloop/poll_pollops.c229
-rw-r--r--src/libmowgli/eventloop/pollable.c88
-rw-r--r--src/libmowgli/eventloop/ports_pollops.c186
-rw-r--r--src/libmowgli/eventloop/qnx_pollops.c200
-rw-r--r--src/libmowgli/eventloop/select_pollops.c203
-rw-r--r--src/libmowgli/eventloop/timer.c172
-rw-r--r--src/libmowgli/eventloop/windows_pollops.c276
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