summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Bühler <stbuehler@web.de>2012-11-05 15:10:29 +0100
committerStefan Bühler <stbuehler@web.de>2012-11-05 15:10:29 +0100
commitf5fc31c6b7d270c108fd9c4b87e4fda4b5af45ba (patch)
tree26f592ab05bb9c79aff035831e63626f55154014
downloadevcon-f5fc31c6b7d270c108fd9c4b87e4fda4b5af45ba.tar.gz
evcon-f5fc31c6b7d270c108fd9c4b87e4fda4b5af45ba.zip
Initial commitHEADmaster
-rw-r--r--.gitignore13
-rw-r--r--COPYING22
-rw-r--r--Makefile.am12
-rw-r--r--README.md65
-rwxr-xr-xautogen.sh31
-rw-r--r--configure.ac126
-rw-r--r--evcon-ev.pc.in11
-rw-r--r--evcon-event.pc.in11
-rw-r--r--evcon-glib.pc.in11
-rw-r--r--evcon.pc.in11
-rw-r--r--libevcon-ev0.symbols2
-rw-r--r--libevcon-event0.symbols2
-rw-r--r--libevcon-glib0.symbols4
-rw-r--r--libevcon0.symbols66
-rw-r--r--src/Makefile.am1
-rw-r--r--src/backend-ev/Makefile.am16
-rw-r--r--src/backend-ev/ev-backend.c165
-rw-r--r--src/backend-ev/evcon-ev.h10
-rw-r--r--src/backend-event/Makefile.am16
-rw-r--r--src/backend-event/evcon-event.h10
-rw-r--r--src/backend-event/event-backend.c157
-rw-r--r--src/backend-glib/Makefile.am16
-rw-r--r--src/backend-glib/evcon-glib.h13
-rw-r--r--src/backend-glib/glib-allocator.c38
-rw-r--r--src/backend-glib/glib-backend.c376
-rw-r--r--src/backend-qt/Makefile.am16
-rw-r--r--src/core/Makefile.am14
-rw-r--r--src/core/evcon-allocator.h35
-rw-r--r--src/core/evcon-backend.h64
-rw-r--r--src/core/evcon-config-private.h.in98
-rw-r--r--src/core/evcon-config.h.in9
-rw-r--r--src/core/evcon.c564
-rw-r--r--src/core/evcon.h114
-rw-r--r--src/tests/Makefile.am33
-rw-r--r--src/tests/evcon-echo.c365
-rw-r--r--src/tests/evcon-echo.h54
-rw-r--r--src/tests/evcon-test-ev.c45
-rw-r--r--src/tests/evcon-test-event.c43
-rw-r--r--src/tests/evcon-test-glib.c47
39 files changed, 2706 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..146e516
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess*
+config.sub*
+configure
+depcomp
+install-sh
+ltmain.sh
+m4/
+missing
+*~
+src/core/evcon-config-private.h.in
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2baf904
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,22 @@
+
+The MIT License
+
+Copyright (c) 2012 Stefan Bühler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..e1affa1
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,12 @@
+
+SUBDIRS = . src
+
+EXTRA_DIST=README.md autogen.sh evcon.pc.in evcon-ev.pc.in evcon-glib.pc.in evcon-event.pc.in
+EXTRA_DIST+=libevcon-ev0.symbols libevcon-event0.symbols libevcon-glib0.symbols libevcon0.symbols
+
+ACLOCAL_AMFLAGS=-I m4
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = evcon.pc evcon-ev.pc evcon-glib.pc evcon-event.pc
+
+$(pkgconfig_DATA): config.status
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c35e194
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+Description
+-----------
+
+evon is a generic wrapper library that sits between libraries that need socket (file descriptor), timeout and (thread safe) asynchronous events, and an application that wants to use the library.
+
+Platforms
+---------
+
+Should work on all POSIX compatible platforms.
+
+Features
+--------
+
+Event types:
+
+* read and write events for asynchronous file descriptors (sockets)
+* simple timeout events
+* (thread safe) asynchronous events (notifications - for example from other threads, that wakeup the event loop)
+
+Backends for:
+
+* [libev](http://software.schmorp.de/pkg/libev.html)
+* [libevent](http://libevent.org/)
+* [glib](http://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html)
+
+
+Simple Scenario
+---------------
+
+You want to write an application that uses two different event based libraries; if these libraries don't use the same event loop (say X uses libev and Y libevent) you will have difficulties using them - you could give each library its own thread for example, but embedding one event loop in another is usually not easy.
+
+If the libraries were built against evcon, you could use any event loop - if there is no backend for it yet, you probably can write one.
+
+Examples
+--------
+
+See `src/tests/evcon-echo.c` and `src/tests/evcon-echo.h` for an example "library", and `src/tests/evcon-test-*.c` for how to use them in an application.
+
+Building from git
+-----------------
+
+Run the following in the source directory to prepare the build system:
+
+ ./autogen.sh
+
+You will need automake and autoconf for this.
+
+Building
+--------
+
+All backends need glib (>= 2.14).
+The libev backend needs libev >= 4, the libevent backend needs libevent >= 2.
+
+Build in a sub directory:
+
+ mkdir build
+ cd build
+ ../configure
+ make check
+
+Install (probably has to be run as root):
+
+ make install
+
+As always it is recommended to use a package system to install files instead (dpkg, rpm, ...).
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..914b3f7
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+LIBTOOLIZE=${LIBTOOLIZE:-libtoolize}
+LIBTOOLIZE_FLAGS="--copy --force"
+ACLOCAL=${ACLOCAL:-aclocal}
+AUTOHEADER=${AUTOHEADER:-autoheader}
+AUTOMAKE=${AUTOMAKE:-automake}
+AUTOMAKE_FLAGS="--add-missing --copy"
+AUTOCONF=${AUTOCONF:-autoconf}
+
+ARGV0=$0
+
+srcdir=$(readlink -f "$0")
+srcdir=$(dirname "$srcdir")
+cd "$srcdir"
+
+set -e
+
+
+run() {
+ echo "$ARGV0: running \`$@'"
+ $@
+}
+
+run $LIBTOOLIZE $LIBTOOLIZE_FLAGS
+run $ACLOCAL $ACLOCAL_FLAGS
+run $AUTOHEADER
+run $AUTOMAKE $AUTOMAKE_FLAGS
+run $AUTOCONF
+echo "Now type './configure ...' and 'make' to compile."
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f581a8a
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,126 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.63])
+AC_INIT([evcon], [0.1.0], [lighttpd@stbuehler.de])
+AC_CONFIG_SRCDIR([src/core/evcon.c])
+AC_CONFIG_HEADERS([src/core/evcon-config-private.h])
+AC_CONFIG_HEADERS([src/core/evcon-config.h])
+
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+
+m4_ifndef([PKG_PROG_PKG_CONFIG], [m4_fatal([pkg-config not installed])])
+m4_ifndef([AC_PROG_LIBTOOL], [m4_fatal([libtool not installed])])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_LIBTOOL
+AC_PROG_MAKE_SET
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+
+# Checks for library functions.
+AC_FUNC_FORK
+AC_CHECK_FUNCS([dup2 pipe2])
+
+# Checks for libraries.
+
+AC_ARG_ENABLE([glib], AS_HELP_STRING([--disable-glib], [Disable building glib wrapper]), [build_glib=no], [build_glib=yes])
+AC_ARG_ENABLE([ev], AS_HELP_STRING([--disable-ev], [Disable building ev wrapper]), [build_ev=no], [build_ev=yes])
+AC_ARG_ENABLE([event], AS_HELP_STRING([--disable-event], [Disable building event wrapper]), [build_event=no], [build_event=yes])
+
+if test "x${build_glib}" != "xno" -o "x${build_ev}" != "xno" -o "x${build_event}" != "xno"; then
+ AC_MSG_CHECKING([Enabled at least one backend. Requires glib.])
+
+ # glib
+ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16.0], [],[AC_MSG_ERROR("glib-2.0 >= 2.16.0 not found")])
+fi
+
+AC_ARG_ENABLE(glib-compat,
+ AC_HELP_STRING([--enable-glib-compat],[build for older glib versions even with new headers]),
+ AC_DEFINE([EVCON_GLIB_COMPAT_API], [1], [build for older glib versions even with new headers]),[])
+
+LIBEV_CFLAGS=""
+LIBEV_LIBS=""
+if test "x${build_ev}" != "xno"; then
+ AC_MSG_CHECKING([Enabled ev wrapper. Requires libev.])
+
+ # libev
+ AC_MSG_CHECKING([for libev support])
+ AC_ARG_WITH([libev],
+ [AS_HELP_STRING([--with-libev@<:@=PATH@:>@],[Search for libev in PATH/include and PATH/lib])],
+ [WITH_LIBEV=$withval],[WITH_LIBEV=yes])
+
+ PKG_CHECK_MODULES([LIBEV], [libev], [], [
+ # no pkg-config for libev, searching manually:
+
+ if test "$WITH_LIBEV" != "yes"; then
+ LIBEV_CFLAGS="-I$WITH_LIBEV/include"
+ LIBEV_LIBS="-L$WITH_LIBEV/lib -lev"
+ else
+ AC_CHECK_HEADERS([ev.h],[
+ AC_CHECK_LIB([ev], [ev_time], [
+ LIBEV_LIBS="-lev"
+ ],[
+ AC_MSG_ERROR([libev not found])
+ ]
+ )],[
+ AC_MSG_ERROR([libev not found])
+ ]
+ )
+ fi
+ ])
+fi
+AC_SUBST([LIBEV_CFLAGS])
+AC_SUBST([LIBEV_LIBS])
+
+if test "x${build_event}" != "xno"; then
+ AC_MSG_CHECKING([Enabled event wrapper. Requires libevent.])
+
+ # event
+ PKG_CHECK_MODULES([LIBEVENT], [libevent >= 2], [
+ # we want event_core, not event
+ LIBEVENT_LIBS=`echo "$LIBEVENT_LIBS" | sed 's#\(^\| \)-levent\($\| \)# -levent_core #'`
+ ],[AC_MSG_ERROR("libevent >= 2 not found")])
+fi
+
+
+AM_CONDITIONAL([BUILD_GLIB], [test "x${build_glib}" != "xno"])
+AM_CONDITIONAL([BUILD_EV], [test "x${build_ev}" != "xno"])
+AM_CONDITIONAL([BUILD_EVENT], [test "x${build_event}" != "xno"])
+
+
+#AC_ARG_ENABLE([qt], AS_HELP_STRING([--disable-qt], [Disable building qt wrapper]), [build_qt=$withval], [build_qt=yes])
+#
+#if test "x${build_qt}" != "xno"; then
+# AC_MSG_CHECKING([Enabled qt wrapper. Requires c++ and qt4.])
+# AC_PROG_CXX
+# PKG_CHECK_MODULES([QT], [QtCore >= 4.0.0], [],[AC_MSG_ERROR("QtCore >= 4.0.0 not found")])
+#fi
+#AM_CONDITIONAL([BUILD_QT], [test "x${build_qt}" != "xno"])
+
+
+# check for extra compiler options (warning options)
+if test "${GCC}" = "yes"; then
+ CFLAGS="${CFLAGS} -Wall -W -Wshadow -pedantic -std=gnu99"
+fi
+
+AC_ARG_ENABLE(extra-warnings,
+ AC_HELP_STRING([--enable-extra-warnings],[enable extra warnings (gcc specific)]),
+ [case "${enableval}" in
+ yes) extrawarnings=true ;;
+ no) extrawarnings=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-extra-warnings) ;;
+ esac],[extrawarnings=false])
+
+if test x$extrawarnings = xtrue; then
+ CFLAGS="${CFLAGS} -g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wno-pointer-sign -Wcast-align -Winline -Wsign-compare -Wnested-externs -Wpointer-arith -Wl,--as-needed -Wformat-security"
+fi
+
+AC_CONFIG_FILES([Makefile src/Makefile src/core/Makefile src/backend-glib/Makefile src/backend-ev/Makefile src/backend-event/Makefile src/tests/Makefile evcon.pc evcon-ev.pc evcon-glib.pc evcon-event.pc])
+AC_OUTPUT
diff --git a/evcon-ev.pc.in b/evcon-ev.pc.in
new file mode 100644
index 0000000..d8f13fa
--- /dev/null
+++ b/evcon-ev.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: evcon-ev
+Description: libev backend for event connector library
+Version: @VERSION@
+Requires: evcon
+Libs: -L${libdir} -levcon-ev
+Cflags:
diff --git a/evcon-event.pc.in b/evcon-event.pc.in
new file mode 100644
index 0000000..e2be16b
--- /dev/null
+++ b/evcon-event.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: evcon-event
+Description: libevent backend for event connector library
+Version: @VERSION@
+Requires: evcon
+Libs: -L${libdir} -levcon-event
+Cflags:
diff --git a/evcon-glib.pc.in b/evcon-glib.pc.in
new file mode 100644
index 0000000..b3e0e58
--- /dev/null
+++ b/evcon-glib.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: evcon-glib
+Description: glib backend for event connector library
+Version: @VERSION@
+Requires: evcon
+Libs: -L${libdir} -levcon-glib
+Cflags:
diff --git a/evcon.pc.in b/evcon.pc.in
new file mode 100644
index 0000000..cc21215
--- /dev/null
+++ b/evcon.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: evcon
+Description: event connector library
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -levcon
+Cflags:
diff --git a/libevcon-ev0.symbols b/libevcon-ev0.symbols
new file mode 100644
index 0000000..e3dd6e2
--- /dev/null
+++ b/libevcon-ev0.symbols
@@ -0,0 +1,2 @@
+libevcon-ev.so.0 libevcon-ev0 #MINVER#
+ evcon_loop_from_ev@Base 0.1.0
diff --git a/libevcon-event0.symbols b/libevcon-event0.symbols
new file mode 100644
index 0000000..39c0227
--- /dev/null
+++ b/libevcon-event0.symbols
@@ -0,0 +1,2 @@
+libevcon-event.so.0 libevcon-event0 #MINVER#
+ evcon_loop_from_event@Base 0.1.0
diff --git a/libevcon-glib0.symbols b/libevcon-glib0.symbols
new file mode 100644
index 0000000..e64ddca
--- /dev/null
+++ b/libevcon-glib0.symbols
@@ -0,0 +1,4 @@
+libevcon-glib.so.0 libevcon-glib0 #MINVER#
+ evcon_glib_allocator@Base 0.1.0
+ evcon_loop_from_glib@Base 0.1.0
+ evcon_loop_glib_get_context@Base 0.1.0
diff --git a/libevcon0.symbols b/libevcon0.symbols
new file mode 100644
index 0000000..5b9ee1a
--- /dev/null
+++ b/libevcon0.symbols
@@ -0,0 +1,66 @@
+libevcon.so.0 libevcon0 #MINVER#
+ evcon_alloc0@Base 0.1.0
+ evcon_alloc@Base 0.1.0
+ evcon_allocator_free@Base 0.1.0
+ evcon_allocator_get_data@Base 0.1.0
+ evcon_allocator_init@Base 0.1.0
+ evcon_allocator_new@Base 0.1.0
+ evcon_allocator_set_data@Base 0.1.0
+ evcon_async_free@Base 0.1.0
+ evcon_async_get_backend_data@Base 0.1.0
+ evcon_async_get_cb@Base 0.1.0
+ evcon_async_get_loop@Base 0.1.0
+ evcon_async_get_user_data@Base 0.1.0
+ evcon_async_new@Base 0.1.0
+ evcon_async_set_backend_data@Base 0.1.0
+ evcon_async_set_cb@Base 0.1.0
+ evcon_async_set_user_data@Base 0.1.0
+ evcon_async_wakeup@Base 0.1.0
+ evcon_backend_free@Base 0.1.0
+ evcon_backend_get_data@Base 0.1.0
+ evcon_backend_init@Base 0.1.0
+ evcon_backend_new@Base 0.1.0
+ evcon_backend_set_data@Base 0.1.0
+ evcon_fd_free@Base 0.1.0
+ evcon_fd_get_backend_data@Base 0.1.0
+ evcon_fd_get_cb@Base 0.1.0
+ evcon_fd_get_events@Base 0.1.0
+ evcon_fd_get_fd@Base 0.1.0
+ evcon_fd_get_loop@Base 0.1.0
+ evcon_fd_get_user_data@Base 0.1.0
+ evcon_fd_is_active@Base 0.1.0
+ evcon_fd_new@Base 0.1.0
+ evcon_fd_set_backend_data@Base 0.1.0
+ evcon_fd_set_cb@Base 0.1.0
+ evcon_fd_set_events@Base 0.1.0
+ evcon_fd_set_fd@Base 0.1.0
+ evcon_fd_set_user_data@Base 0.1.0
+ evcon_fd_start@Base 0.1.0
+ evcon_fd_stop@Base 0.1.0
+ evcon_feed_async@Base 0.1.0
+ evcon_feed_fd@Base 0.1.0
+ evcon_feed_timer@Base 0.1.0
+ evcon_free@Base 0.1.0
+ evcon_init_fd@Base 0.1.0
+ evcon_loop_get_allocator@Base 0.1.0
+ evcon_loop_get_backend_data@Base 0.1.0
+ evcon_loop_new@Base 0.1.0
+ evcon_loop_ref@Base 0.1.0
+ evcon_loop_set_backend_data@Base 0.1.0
+ evcon_loop_unref@Base 0.1.0
+ evcon_timer_free@Base 0.1.0
+ evcon_timer_get_backend_data@Base 0.1.0
+ evcon_timer_get_cb@Base 0.1.0
+ evcon_timer_get_loop@Base 0.1.0
+ evcon_timer_get_repeat@Base 0.1.0
+ evcon_timer_get_timeout@Base 0.1.0
+ evcon_timer_get_user_data@Base 0.1.0
+ evcon_timer_is_active@Base 0.1.0
+ evcon_timer_new@Base 0.1.0
+ evcon_timer_once@Base 0.1.0
+ evcon_timer_repeat@Base 0.1.0
+ evcon_timer_set_backend_data@Base 0.1.0
+ evcon_timer_set_cb@Base 0.1.0
+ evcon_timer_set_repeat@Base 0.1.0
+ evcon_timer_set_user_data@Base 0.1.0
+ evcon_timer_stop@Base 0.1.0
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..65a5867
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = core backend-glib backend-ev backend-event tests
diff --git a/src/backend-ev/Makefile.am b/src/backend-ev/Makefile.am
new file mode 100644
index 0000000..2577680
--- /dev/null
+++ b/src/backend-ev/Makefile.am
@@ -0,0 +1,16 @@
+AM_CFLAGS=-I$(srcdir)/../core
+
+install_libs=
+install_headers=
+
+if BUILD_EV
+install_libs += libevcon-ev.la
+install_headers += evcon-ev.h
+libevcon_ev_la_CPPFLAGS = $(GLIB_CFLAGS) $(LIBEV_CFLAGS)
+libevcon_ev_la_LDFLAGS = -export-dynamic -no-undefined $(GLIB_LIBS) $(LIBEV_LIBS)
+libevcon_ev_la_SOURCES = ev-backend.c
+libevcon_ev_la_LIBADD = ../core/libevcon.la
+endif
+
+lib_LTLIBRARIES = $(install_libs)
+include_HEADERS = $(install_headers)
diff --git a/src/backend-ev/ev-backend.c b/src/backend-ev/ev-backend.c
new file mode 100644
index 0000000..8c4de7c
--- /dev/null
+++ b/src/backend-ev/ev-backend.c
@@ -0,0 +1,165 @@
+
+#include <evcon-ev.h>
+
+#include <evcon-allocator.h>
+#include <evcon-backend.h>
+
+#include <evcon-config-private.h>
+
+#include <glib.h>
+
+#define UNUSED(x) ((void)(x))
+
+/* ev loop wrapper */
+
+static void evcon_ev_free_loop(evcon_loop *loop, void *loop_data, void *backend_data) {
+ UNUSED(loop);
+ UNUSED(backend_data);
+ UNUSED(loop_data);
+}
+
+static void evcon_ev_fd_cb(struct ev_loop *loop, ev_io *w, int revents) {
+ evcon_fd_watcher *watcher = (evcon_fd_watcher*) w->data;
+ int events;
+ UNUSED(loop);
+
+ events = 0;
+ if (0 != (revents & EV_ERROR)) events |= EVCON_ERROR;
+ if (0 != (revents & EV_READ)) events |= EVCON_READ;
+ if (0 != (revents & EV_WRITE)) events |= EVCON_WRITE;
+
+ evcon_feed_fd(watcher, events);
+}
+
+static void evcon_ev_fd_update(evcon_fd_watcher *watcher, evcon_fd fd, int events, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ ev_io *w = (ev_io*) watcher_data;
+ struct ev_loop *evl = (struct ev_loop*) loop_data;
+ int evs;
+
+ if (-1 == fd) {
+ /* delete watcher */
+ if (NULL == w) return;
+
+ ev_io_stop(evl, w);
+ evcon_free(allocator, w, sizeof(*w));
+ evcon_fd_set_backend_data(watcher, NULL);
+ return;
+ }
+
+ evs = 0;
+ if (0 != (events & EVCON_READ)) evs |= EV_READ;
+ if (0 != (events & EVCON_WRITE)) evs |= EV_WRITE;
+
+ if (NULL == w) {
+ w = evcon_alloc0(allocator, sizeof(ev_io));
+ evcon_fd_set_backend_data(watcher, w);
+ ev_io_init(w, evcon_ev_fd_cb, fd, evs);
+ w->data = watcher;
+ if (0 != evs) ev_io_start(evl, w);
+ return;
+ }
+
+ if (w->events == evs && fd == w->fd) return;
+
+ ev_io_stop(evl, w);
+ ev_io_set(w, fd, evs);
+ if (0 != evs) ev_io_start(evl, w);
+}
+
+static void evcon_ev_timer_cb(struct ev_loop *loop, ev_timer *w, int revents) {
+ evcon_timer_watcher *watcher = (evcon_timer_watcher*) w->data;
+ UNUSED(loop);
+ UNUSED(revents);
+
+ evcon_feed_timer(watcher);
+}
+
+static void evcon_ev_timer_update(evcon_timer_watcher *watcher, evcon_interval timeout, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ ev_timer *w = (ev_timer*) watcher_data;
+ struct ev_loop *evl = (struct ev_loop*) loop_data;
+
+ if (-2 == timeout) {
+ /* delete watcher */
+ if (NULL == w) return;
+
+ ev_timer_stop(evl, w);
+ evcon_free(allocator, w, sizeof(*w));
+ evcon_timer_set_backend_data(watcher, NULL);
+ return;
+ }
+
+ if (-1 == timeout && NULL == w) return;
+
+ if (-1 == timeout) {
+ ev_timer_stop(evl, w);
+ return;
+ }
+
+ if (NULL == w) {
+ w = evcon_alloc0(allocator, sizeof(ev_timer));
+ evcon_timer_set_backend_data(watcher, w);
+ ev_timer_init(w, evcon_ev_timer_cb, EVCON_INTERVAL_AS_DOUBLE_SEC(timeout), 0.);
+ w->data = watcher;
+ ev_timer_start(evl, w);
+ return;
+ }
+
+ ev_timer_stop(evl, w);
+ ev_timer_set(w, EVCON_INTERVAL_AS_DOUBLE_SEC(timeout), 0.);
+ ev_timer_start(evl, w);
+}
+
+static void evcon_ev_async_cb(struct ev_loop *loop, ev_async *w, int revents) {
+ evcon_async_watcher *watcher = (evcon_async_watcher*) w->data;
+ UNUSED(loop);
+ UNUSED(revents);
+
+ evcon_feed_async(watcher);
+}
+
+static void evcon_ev_async_update(evcon_async_watcher *watcher, evcon_async_func f, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ ev_async *w = (ev_async*) watcher_data;
+ struct ev_loop *evl = (struct ev_loop*) loop_data;
+
+ switch (f) {
+ case EVCON_ASYNC_TRIGGER:
+ ev_async_send(evl, w);
+ break;
+ case EVCON_ASYNC_NEW:
+ w = evcon_alloc0(allocator, sizeof(ev_async));
+ evcon_async_set_backend_data(watcher, w);
+ ev_async_init(w, evcon_ev_async_cb);
+ w->data = watcher;
+ ev_async_start(evl, w);
+ break;
+ case EVCON_ASYNC_FREE:
+ if (NULL == w) return;
+
+ ev_async_stop(evl, w);
+ evcon_free(allocator, w, sizeof(*w));
+ evcon_async_set_backend_data(watcher, NULL);
+ return;
+ }
+}
+
+static evcon_backend* evcon_ev_backend(evcon_allocator* allocator) {
+ static char static_backend_buf[EVCON_BACKEND_RECOMMENDED_SIZE];
+ static volatile evcon_backend* backend = NULL;
+
+ if (g_once_init_enter(&backend)) {
+ evcon_backend* bcknd = evcon_backend_init(static_backend_buf, sizeof(static_backend_buf), NULL, allocator, evcon_ev_free_loop, evcon_ev_fd_update, evcon_ev_timer_update, evcon_ev_async_update);
+
+ g_once_init_leave(&backend, bcknd);
+ }
+
+ return (evcon_backend*) backend;
+}
+
+evcon_loop* evcon_loop_from_ev(struct ev_loop *loop, evcon_allocator* allocator) {
+ evcon_backend *backend = evcon_ev_backend(allocator);
+ evcon_loop *evc_loop = evcon_loop_new(backend, allocator);
+
+ evcon_loop_set_backend_data(evc_loop, loop);
+
+ return evc_loop;
+}
diff --git a/src/backend-ev/evcon-ev.h b/src/backend-ev/evcon-ev.h
new file mode 100644
index 0000000..2426653
--- /dev/null
+++ b/src/backend-ev/evcon-ev.h
@@ -0,0 +1,10 @@
+#ifndef __EVCON_EVCON_EV_H
+#define __EVCON_EVCON_EV_H __EVCON_EVCON_EV_H
+
+#include <evcon.h>
+
+#include <ev.h>
+
+evcon_loop* evcon_loop_from_ev(struct ev_loop *loop, evcon_allocator* allocator);
+
+#endif
diff --git a/src/backend-event/Makefile.am b/src/backend-event/Makefile.am
new file mode 100644
index 0000000..a8ffe57
--- /dev/null
+++ b/src/backend-event/Makefile.am
@@ -0,0 +1,16 @@
+AM_CFLAGS=-I$(srcdir)/../core
+
+install_libs=
+install_headers=
+
+if BUILD_EVENT
+install_libs += libevcon-event.la
+install_headers += evcon-event.h
+libevcon_event_la_CPPFLAGS = $(GLIB_CFLAGS) $(LIBEVENT_CFLAGS)
+libevcon_event_la_LDFLAGS = -export-dynamic -no-undefined $(GLIB_LIBS) $(LIBEVENT_LIBS)
+libevcon_event_la_SOURCES = event-backend.c
+libevcon_event_la_LIBADD = ../core/libevcon.la
+endif
+
+lib_LTLIBRARIES = $(install_libs)
+include_HEADERS = $(install_headers)
diff --git a/src/backend-event/evcon-event.h b/src/backend-event/evcon-event.h
new file mode 100644
index 0000000..010a10b
--- /dev/null
+++ b/src/backend-event/evcon-event.h
@@ -0,0 +1,10 @@
+#ifndef __EVCON_EVCON_EVENT_H
+#define __EVCON_EVCON_EVENT_H __EVCON_EVCON_EVENT_H
+
+#include <evcon.h>
+
+#include <event2/event.h>
+
+evcon_loop* evcon_loop_from_event(struct event_base *base, evcon_allocator* allocator);
+
+#endif
diff --git a/src/backend-event/event-backend.c b/src/backend-event/event-backend.c
new file mode 100644
index 0000000..ab6328a
--- /dev/null
+++ b/src/backend-event/event-backend.c
@@ -0,0 +1,157 @@
+
+#include <evcon-event.h>
+
+#include <evcon-allocator.h>
+#include <evcon-backend.h>
+
+#include <evcon-config-private.h>
+
+#include <glib.h>
+
+#define UNUSED(x) ((void)(x))
+
+/* event loop wrapper */
+
+static void evcon_event_free_loop(evcon_loop *loop, void *loop_data, void *backend_data) {
+ UNUSED(loop);
+ UNUSED(backend_data);
+ UNUSED(loop_data);
+}
+
+static void evcon_event_fd_cb(evutil_socket_t fd, short revents, void *user_data) {
+ evcon_fd_watcher *watcher = (evcon_fd_watcher*) user_data;
+ int events;
+ UNUSED(fd);
+
+ events = 0;
+ if (0 != (revents & EV_READ)) events |= EVCON_READ;
+ if (0 != (revents & EV_WRITE)) events |= EVCON_WRITE;
+
+ evcon_feed_fd(watcher, events);
+}
+
+static void evcon_event_fd_update(evcon_fd_watcher *watcher, evcon_fd fd, int events, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ struct event *w = (struct event*) watcher_data;
+ struct event_base *base = (struct event_base*) loop_data;
+ short evs;
+ UNUSED(allocator);
+
+ if (-1 == fd) {
+ /* delete watcher */
+ if (NULL == w) return;
+
+ event_free(w);
+ evcon_fd_set_backend_data(watcher, NULL);
+ return;
+ }
+
+ evs = EV_PERSIST;
+ if (0 != (events & EVCON_READ)) evs |= EV_READ;
+ if (0 != (events & EVCON_WRITE)) evs |= EV_WRITE;
+
+ if (NULL == w) {
+ w = event_new(base, fd, evs, evcon_event_fd_cb, watcher);
+ evcon_fd_set_backend_data(watcher, w);
+ if (EV_PERSIST != evs) event_add(w, NULL);
+ return;
+ }
+
+ if (event_get_events(w) == evs && event_get_fd(w) == fd) return;
+
+ event_del(w);
+ event_assign(w, base, fd, evs, evcon_event_fd_cb, watcher);
+ if (EV_PERSIST != evs) event_add(w, NULL);
+}
+
+static void evcon_event_timer_cb(evutil_socket_t fd, short revents, void *user_data) {
+ evcon_timer_watcher *watcher = (evcon_timer_watcher*) user_data;
+ UNUSED(fd);
+ UNUSED(revents);
+
+ evcon_feed_timer(watcher);
+}
+
+static void evcon_event_timer_update(evcon_timer_watcher *watcher, evcon_interval timeout, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ struct event *w = (struct event*) watcher_data;
+ struct event_base *base = (struct event_base*) loop_data;
+ struct timeval tv;
+ UNUSED(allocator);
+
+ if (-2 == timeout) {
+ /* delete watcher */
+ if (NULL == w) return;
+
+ event_free(w);
+ evcon_timer_set_backend_data(watcher, NULL);
+ return;
+ }
+
+ if (-1 == timeout && NULL == w) return;
+
+ if (-1 == timeout) {
+ event_del(w);
+ return;
+ }
+
+ if (NULL == w) {
+ w = event_new(base, -1, EV_TIMEOUT, evcon_event_timer_cb, watcher);
+ evcon_timer_set_backend_data(watcher, w);
+ }
+
+ tv.tv_sec = EVCON_INTERVAL_AS_SEC(timeout);
+ //tv.tv_usec = EVCON_INTERVAL_AS_USEC(timeout) % 1000000;
+ event_add(w, &tv);
+}
+
+static void evcon_event_async_cb(evutil_socket_t fd, short revents, void *user_data) {
+ evcon_async_watcher *watcher = (evcon_async_watcher*) user_data;
+ UNUSED(fd);
+ UNUSED(revents);
+
+ evcon_feed_async(watcher);
+}
+
+static void evcon_event_async_update(evcon_async_watcher *watcher, evcon_async_func f, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ struct event *w = (struct event*) watcher_data;
+ struct event_base *base = (struct event_base*) loop_data;
+ UNUSED(allocator);
+
+ switch (f) {
+ case EVCON_ASYNC_TRIGGER:
+ event_active(w, EV_SIGNAL, 0);
+ break;
+ case EVCON_ASYNC_NEW:
+ w = event_new(base, -1, EV_PERSIST, evcon_event_async_cb, watcher);
+ evcon_async_set_backend_data(watcher, w);
+ event_add(w, NULL);
+ break;
+ case EVCON_ASYNC_FREE:
+ if (NULL == w) return;
+
+ event_free(w);
+ evcon_async_set_backend_data(watcher, NULL);
+ return;
+ }
+}
+
+static evcon_backend* evcon_event_backend(evcon_allocator* allocator) {
+ static char static_backend_buf[EVCON_BACKEND_RECOMMENDED_SIZE];
+ static volatile evcon_backend* backend = NULL;
+
+ if (g_once_init_enter(&backend)) {
+ evcon_backend* bcknd = evcon_backend_init(static_backend_buf, sizeof(static_backend_buf), NULL, allocator, evcon_event_free_loop, evcon_event_fd_update, evcon_event_timer_update, evcon_event_async_update);
+
+ g_once_init_leave(&backend, bcknd);
+ }
+
+ return (evcon_backend*) backend;
+}
+
+evcon_loop* evcon_loop_from_event(struct event_base *base, evcon_allocator* allocator) {
+ evcon_backend *backend = evcon_event_backend(allocator);
+ evcon_loop *evc_loop = evcon_loop_new(backend, allocator);
+
+ evcon_loop_set_backend_data(evc_loop, base);
+
+ return evc_loop;
+}
diff --git a/src/backend-glib/Makefile.am b/src/backend-glib/Makefile.am
new file mode 100644
index 0000000..00a6aaa
--- /dev/null
+++ b/src/backend-glib/Makefile.am
@@ -0,0 +1,16 @@
+AM_CFLAGS=-I$(srcdir)/../core
+
+install_libs=
+install_headers=
+
+if BUILD_GLIB
+install_libs += libevcon-glib.la
+install_headers += evcon-glib.h
+libevcon_glib_la_CPPFLAGS = $(GLIB_CFLAGS)
+libevcon_glib_la_LDFLAGS = -export-dynamic -no-undefined $(GLIB_LIBS)
+libevcon_glib_la_SOURCES = glib-allocator.c glib-backend.c
+libevcon_glib_la_LIBADD = ../core/libevcon.la
+endif
+
+lib_LTLIBRARIES = $(install_libs)
+include_HEADERS = $(install_headers)
diff --git a/src/backend-glib/evcon-glib.h b/src/backend-glib/evcon-glib.h
new file mode 100644
index 0000000..6efa52c
--- /dev/null
+++ b/src/backend-glib/evcon-glib.h
@@ -0,0 +1,13 @@
+#ifndef __EVCON_EVCON_GLIB_H
+#define __EVCON_EVCON_GLIB_H __EVCON_EVCON_GLIB_H
+
+#include <evcon.h>
+
+#include <glib.h>
+
+evcon_allocator* evcon_glib_allocator(void);
+evcon_loop* evcon_loop_from_glib(GMainContext *ctx, evcon_allocator *allocator);
+
+GMainContext* evcon_loop_glib_get_context(evcon_loop *loop);
+
+#endif
diff --git a/src/backend-glib/glib-allocator.c b/src/backend-glib/glib-allocator.c
new file mode 100644
index 0000000..62b8902
--- /dev/null
+++ b/src/backend-glib/glib-allocator.c
@@ -0,0 +1,38 @@
+
+#include <evcon-glib.h>
+
+#include <evcon-allocator.h>
+
+#include <evcon-config-private.h>
+
+#define UNUSED(x) ((void)(x))
+
+/* GLib slice wrapper */
+
+static void* evcon_glib_alloc_cb(size_t size, void* user_data) {
+ UNUSED(user_data);
+ return g_slice_alloc(size);
+}
+
+static void evcon_glib_free_cb(void *ptr, size_t size, void *user_data) {
+ UNUSED(user_data);
+ g_slice_free1(size, ptr);
+}
+
+evcon_allocator* evcon_glib_allocator(void) {
+ static char static_allocator_buf[EVCON_ALLOCATOR_RECOMMENDED_SIZE];
+ static volatile evcon_allocator* allocator = NULL;
+ static volatile int lock = 0;
+
+ if (2 == g_atomic_int_get(&lock)) return (evcon_allocator*) allocator;
+
+ while (!g_atomic_int_compare_and_exchange(&lock, 0, 1)) {
+ if (2 == g_atomic_int_get(&lock)) return (evcon_allocator*) allocator;
+ }
+
+ allocator = evcon_allocator_init(static_allocator_buf, sizeof(static_allocator_buf), NULL, evcon_glib_alloc_cb, evcon_glib_free_cb);
+
+ g_atomic_int_set(&lock, 2);
+
+ return (evcon_allocator*) allocator;
+}
diff --git a/src/backend-glib/glib-backend.c b/src/backend-glib/glib-backend.c
new file mode 100644
index 0000000..6533b45
--- /dev/null
+++ b/src/backend-glib/glib-backend.c
@@ -0,0 +1,376 @@
+
+#define _GNU_SOURCE
+
+#include <evcon-glib.h>
+
+#include <evcon-allocator.h>
+#include <evcon-backend.h>
+
+#include <evcon-config-private.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define UNUSED(x) ((void)(x))
+
+#ifndef EVCON_GLIB_COMPAT_API
+# if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 31
+# define EVCON_GLIB_COMPAT_API 1
+# endif
+#endif
+
+#ifdef EVCON_GLIB_COMPAT_API
+
+typedef GMutex* evcon_glib_mutex;
+
+static void evcon_glib_mutex_init(evcon_glib_mutex *m) {
+ *m = (*g_thread_functions_for_glib_use.mutex_new)();
+}
+
+static void evcon_glib_mutex_clear(evcon_glib_mutex *m) {
+ GMutex *mx = *m;
+ if (g_thread_supported()) (*g_thread_functions_for_glib_use.mutex_free)(mx);
+ *m = NULL;
+}
+
+static void evcon_glib_mutex_lock(evcon_glib_mutex *m) {
+ GMutex *mx = *m;
+ if (g_thread_supported()) (*g_thread_functions_for_glib_use.mutex_lock)(mx);
+}
+
+static void evcon_glib_mutex_unlock(evcon_glib_mutex *m) {
+ GMutex *mx = *m;
+ if (g_thread_supported()) (*g_thread_functions_for_glib_use.mutex_unlock)(mx);
+}
+
+#else
+
+typedef GMutex evcon_glib_mutex;
+
+static void evcon_glib_mutex_init(evcon_glib_mutex *m) {
+ g_mutex_init(m);
+}
+
+static void evcon_glib_mutex_clear(evcon_glib_mutex *m) {
+ g_mutex_clear(m);
+}
+
+static void evcon_glib_mutex_lock(evcon_glib_mutex *m) {
+ g_mutex_lock(m);
+}
+
+static void evcon_glib_mutex_unlock(evcon_glib_mutex *m) {
+ g_mutex_unlock(m);
+}
+
+#endif
+
+/* GLib loop wrapper */
+
+typedef struct evcon_glib_data evcon_glib_data;
+typedef struct evcon_glib_fd_source evcon_glib_fd_source;
+typedef struct evcon_glib_async_watcher evcon_glib_async_watcher;
+
+struct evcon_glib_data {
+ GMainContext *ctx;
+
+ gint async_pipe_fds[2];
+ evcon_fd_watcher *async_watcher;
+ evcon_glib_mutex async_mutex;
+ GQueue async_pending;
+};
+
+struct evcon_glib_fd_source {
+ GSource source;
+ GPollFD pollfd;
+ evcon_fd_watcher *watcher;
+};
+
+struct evcon_glib_async_watcher {
+ GList pending_link;
+ evcon_async_watcher *orig;
+ gboolean active;
+};
+
+static void evcon_glib_free_loop(evcon_loop *loop, void *loop_data, void *backend_data) {
+ evcon_glib_data *data = (evcon_glib_data*) loop_data;
+ UNUSED(loop);
+ UNUSED(backend_data);
+
+ evcon_loop_ref(loop);
+ evcon_fd_free(data->async_watcher);
+
+ close(data->async_pipe_fds[0]); data->async_pipe_fds[0] = -1;
+ close(data->async_pipe_fds[1]); data->async_pipe_fds[1] = -1;
+
+ g_main_context_ref(data->ctx);
+ evcon_glib_mutex_clear(&data->async_mutex);
+ g_slice_free(evcon_glib_data, data);
+}
+
+/* own FD poll handling */
+
+static gboolean fd_source_prepare(GSource *source, gint *timeout);
+static gboolean fd_source_check(GSource *source);
+static gboolean fd_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data);
+static void fd_source_finalize(GSource *source);
+
+static GSourceFuncs fd_source_funcs = {
+ fd_source_prepare,
+ fd_source_check,
+ fd_source_dispatch,
+ fd_source_finalize, 0, 0
+};
+
+static gboolean fd_source_prepare(GSource *source, gint *timeout) {
+ UNUSED(source);
+ *timeout = -1;
+ return FALSE;
+}
+static gboolean fd_source_check(GSource *source) {
+ evcon_glib_fd_source *watch = (evcon_glib_fd_source*) source;
+ return 0 != (watch->pollfd.revents & watch->pollfd.events);
+}
+static gboolean fd_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
+ evcon_glib_fd_source *watch = (evcon_glib_fd_source*) source;
+ int events;
+ UNUSED(callback);
+ UNUSED(user_data);
+
+ events = 0;
+ watch->pollfd.revents &= watch->pollfd.events;
+ if (0 != (watch->pollfd.revents & (G_IO_IN | G_IO_HUP))) events |= EVCON_READ;
+ if (0 != (watch->pollfd.revents & G_IO_ERR)) events |= EVCON_ERROR;
+ if (0 != (watch->pollfd.revents & G_IO_OUT)) events |= EVCON_WRITE;
+
+ if (0 != events) evcon_feed_fd(watch->watcher, events);
+
+ return TRUE;
+}
+static void fd_source_finalize(GSource *source) {
+ UNUSED(source);
+}
+
+static GSource* fd_source_new(evcon_fd_watcher *watcher) {
+ GSource *source = g_source_new(&fd_source_funcs, sizeof(evcon_glib_fd_source));
+ evcon_glib_fd_source *watch = (evcon_glib_fd_source*) source;
+ watch->watcher = watcher;
+ watch->pollfd.fd = -1;
+ watch->pollfd.events = watch->pollfd.revents = 0;
+ evcon_fd_set_backend_data(watcher, watch);
+ return source;
+}
+
+static void evcon_glib_fd_update(evcon_fd_watcher *watcher, evcon_fd fd, int events, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ GMainContext *ctx = ((evcon_glib_data*) loop_data)->ctx;
+ GSource *source = (GSource*) watcher_data;
+ evcon_glib_fd_source *watch;
+ int evs;
+ UNUSED(allocator);
+
+ if (-1 == fd) {
+ /* delete watcher */
+ if (NULL == source) return;
+ g_source_destroy(source);
+ g_source_unref(source);
+ return;
+ }
+
+ if (NULL == source) {
+ source = fd_source_new(watcher);
+ g_source_attach(source, ctx);
+ watch = (evcon_glib_fd_source*) source;
+ g_source_add_poll(source, &watch->pollfd);
+ } else {
+ watch = (evcon_glib_fd_source*) source;
+ }
+
+ evs = 0;
+ if (0 != (events & EVCON_READ)) evs |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+ if (0 != (events & EVCON_WRITE)) evs |= G_IO_OUT | G_IO_ERR;
+
+ if (fd == watch->pollfd.fd && evs == watch->pollfd.events) return;
+
+ if (fd != watch->pollfd.fd) watch->pollfd.revents = 0;
+
+ watch->pollfd.fd = fd;
+ watch->pollfd.events = evs;
+}
+
+static gboolean evcon_glib_timer_cb(gpointer data) {
+ evcon_timer_watcher *watcher = (evcon_timer_watcher*) data;
+ evcon_feed_timer(watcher);
+ return FALSE;
+}
+
+static void evcon_glib_timer_update(evcon_timer_watcher *watcher, evcon_interval timeout, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ GMainContext *ctx = ((evcon_glib_data*) loop_data)->ctx;
+ GSource *source = (GSource*) watcher_data;
+ UNUSED(allocator);
+
+ if (NULL != source) {
+ /* delete old source */
+ g_source_destroy(source);
+ g_source_unref(source);
+ }
+
+ if (timeout < 0) return;
+
+ if (timeout == 0) {
+ source = g_idle_source_new();
+ } else {
+ source = g_timeout_source_new(EVCON_INTERVAL_AS_MSEC(timeout));
+ }
+ evcon_timer_set_backend_data(watcher, source);
+ g_source_set_callback(source, evcon_glib_timer_cb, watcher, NULL);
+ g_source_attach(source, ctx);
+}
+
+
+static void evcon_glib_async_update(evcon_async_watcher *watcher, evcon_async_func f, evcon_allocator *allocator, void *loop_data, void *watcher_data) {
+ static const char val = 'A';
+ evcon_glib_data *data = (evcon_glib_data*) loop_data;
+ evcon_glib_async_watcher *w = (evcon_glib_async_watcher*) watcher_data;
+ UNUSED(allocator);
+
+ evcon_glib_mutex_lock(&data->async_mutex);
+
+ switch (f) {
+ case EVCON_ASYNC_TRIGGER:
+ if (!w->active) {
+ w->active = TRUE;
+ if (0 == data->async_pending.length) {
+ int r;
+trigger_again:
+ r = write(data->async_pipe_fds[1], &val, sizeof(val));
+ if (-1 == r) {
+ switch (errno) {
+ case EINTR:
+ goto trigger_again;
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ break; /* enough data in the pipe to trigger */
+ default:
+ g_error("async wake write failed: %s", g_strerror(errno));
+ }
+ }
+ }
+ g_queue_push_tail_link(&data->async_pending, &w->pending_link);
+ }
+ break;
+ case EVCON_ASYNC_NEW:
+ w = g_slice_new0(evcon_glib_async_watcher);
+ w->pending_link.data = w;
+ w->orig = watcher;
+ evcon_async_set_backend_data(watcher, w);
+ break;
+ case EVCON_ASYNC_FREE:
+ if (NULL == w) goto exit;
+
+ if (w->active) {
+ g_queue_unlink(&data->async_pending, &w->pending_link);
+ w->active = FALSE;
+ }
+ g_slice_free(evcon_glib_async_watcher, w);
+ evcon_async_set_backend_data(watcher, NULL);
+ goto exit;
+ }
+
+exit:
+ evcon_glib_mutex_unlock(&data->async_mutex);
+}
+
+static void evcon_glib_async_cb(evcon_loop *loop, evcon_fd_watcher *watcher, evcon_fd fd, int revents, void* user_data) {
+ evcon_glib_data *data = user_data;
+ evcon_glib_async_watcher *w;
+ char buf[32];
+ UNUSED(loop);
+ UNUSED(watcher);
+ UNUSED(revents);
+
+ (void) read(fd, buf, sizeof(buf));
+
+ for (;;) {
+ {
+ GList *link;
+ evcon_glib_mutex_lock(&data->async_mutex);
+ link = g_queue_pop_head_link(&data->async_pending);
+ if (NULL != link) {
+ w = (evcon_glib_async_watcher*) link->data;
+ w->active = FALSE;
+ } else {
+ w = NULL;
+ }
+ evcon_glib_mutex_unlock(&data->async_mutex);
+ }
+
+ if (NULL == w) break;
+
+ evcon_feed_async(w->orig);
+ }
+}
+
+static evcon_backend* evcon_glib_backend(void) {
+ static char static_backend_buf[EVCON_BACKEND_RECOMMENDED_SIZE];
+ static volatile evcon_backend* backend = NULL;
+
+ if (g_once_init_enter(&backend)) {
+ evcon_backend* bcknd = evcon_backend_init(static_backend_buf, sizeof(static_backend_buf), NULL, evcon_glib_allocator(), evcon_glib_free_loop, evcon_glib_fd_update, evcon_glib_timer_update, evcon_glib_async_update);
+
+ g_once_init_leave(&backend, bcknd);
+ }
+
+ return (evcon_backend*) backend;
+}
+
+static gboolean setup_pipe(int fds[2]) {
+#ifdef HAVE_PIPE2
+ if (-1 == pipe2(fds, O_NONBLOCK | O_CLOEXEC)) {
+ g_error("Cannot create pipe: %s\n", g_strerror(errno));
+ return FALSE;
+ }
+#else
+ if (-1 == pipe(fds)) {
+ g_error("Cannot create pipe: %s\n", g_strerror(errno));
+ return FALSE;
+ }
+
+ evcon_init_fd(fds[0]);
+ evcon_init_fd(fds[1]);
+#endif
+ return TRUE;
+}
+
+evcon_loop* evcon_loop_from_glib(GMainContext *ctx, evcon_allocator *allocator) {
+ evcon_backend *backend;
+ evcon_glib_data *loop_data;
+ evcon_loop *evc_loop;
+ int async_pipe_fds[2];
+
+ if (!setup_pipe(async_pipe_fds)) return NULL;
+
+ if (NULL == allocator) allocator = evcon_glib_allocator();
+
+ backend = evcon_glib_backend();
+ loop_data = g_slice_new0(evcon_glib_data);
+ evc_loop = evcon_loop_new(backend, allocator);
+
+ g_main_context_ref(ctx);
+ loop_data->ctx = ctx;
+ loop_data->async_pipe_fds[0] = async_pipe_fds[0];
+ loop_data->async_pipe_fds[1] = async_pipe_fds[1];
+ evcon_glib_mutex_init(&loop_data->async_mutex);
+ evcon_loop_set_backend_data(evc_loop, loop_data);
+
+ loop_data->async_watcher = evcon_fd_new(evc_loop, evcon_glib_async_cb, async_pipe_fds[0], EVCON_READ, loop_data);
+ evcon_loop_unref(evc_loop);
+
+ return evc_loop;
+}
+
+GMainContext* evcon_loop_glib_get_context(evcon_loop *loop) {
+ return ((evcon_glib_data*) evcon_loop_get_backend_data(loop))->ctx;
+}
diff --git a/src/backend-qt/Makefile.am b/src/backend-qt/Makefile.am
new file mode 100644
index 0000000..2a81223
--- /dev/null
+++ b/src/backend-qt/Makefile.am
@@ -0,0 +1,16 @@
+AM_CFLAGS=-I$(srcdir)/../core
+
+install_libs=
+install_headers=
+
+if BUILD_QT
+install_libs += libevcon-qt.la
+install_headers += evcon-qt.h
+libevcon_qt_la_CPPFLAGS = $(QT_CFLAGS)
+libevcon_qt_la_LDFLAGS = -export-dynamic -no-undefined $(QT_LIBS)
+libevcon_qt_la_SOURCES = evcon-qt.cpp
+libevcon_qt_la_LIBADD = ../core/libevcon.la
+endif
+
+lib_LTLIBRARIES = $(install_libs)
+include_HEADERS = $(install_headers)
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
new file mode 100644
index 0000000..b7be009
--- /dev/null
+++ b/src/core/Makefile.am
@@ -0,0 +1,14 @@
+
+install_libs=
+install_headers=
+
+install_libs += libevcon.la
+install_headers += evcon.h evcon-config.h evcon-allocator.h evcon-backend.h
+libevcon_la_LDFLAGS = -export-dynamic -no-undefined
+libevcon_la_SOURCES = evcon.c
+
+lib_LTLIBRARIES = $(install_libs)
+include_HEADERS = $(install_headers)
+
+dist-hook:
+ rm -f $(distdir)/evcon-config.h
diff --git a/src/core/evcon-allocator.h b/src/core/evcon-allocator.h
new file mode 100644
index 0000000..1cd9ad6
--- /dev/null
+++ b/src/core/evcon-allocator.h
@@ -0,0 +1,35 @@
+#ifndef __EVCON_EVCON_ALLOCATOR_H
+#define __EVCON_EVCON_ALLOCATOR_H __EVCON_EVCON_ALLOCATOR_H
+
+#include <evcon.h>
+
+/* Slab allocator */
+
+/*
+ * public interface
+ */
+
+void* evcon_alloc(evcon_allocator* allocator, size_t size);
+void* evcon_alloc0(evcon_allocator* allocator, size_t size);
+void evcon_free(evcon_allocator* allocator, void* ptr, size_t size);
+
+/*
+ * implementation interface
+ */
+
+typedef void* (*evcon_alloc_cb)(size_t size, void* user_data);
+typedef void (*evcon_free_cb)(void *ptr, size_t size, void *user_data);
+
+evcon_allocator* evcon_allocator_new(void* user_data, evcon_alloc_cb alloc_cb, evcon_free_cb free_cb);
+void evcon_allocator_free(evcon_allocator* allocator); /* freeing the allocator should be the last thing your app does */
+
+#define EVCON_ALLOCATOR_RECOMMENDED_SIZE (4*sizeof(void*))
+/* if memsize is large enough to contain a backend, initialize it and returns mem. otherwise allocates a new block */
+evcon_allocator* evcon_allocator_init(char *mem, size_t memsize,
+ void *user_data, evcon_alloc_cb alloc_cb, evcon_free_cb free_cb);
+
+/* allows to change user_data */
+void* evcon_allocator_get_data(evcon_allocator *allocator);
+void evcon_allocator_set_data(evcon_allocator *allocator, void *user_data);
+
+#endif
diff --git a/src/core/evcon-backend.h b/src/core/evcon-backend.h
new file mode 100644
index 0000000..ce30311
--- /dev/null
+++ b/src/core/evcon-backend.h
@@ -0,0 +1,64 @@
+#ifndef __EVCON_EVCON_BACKEND_H
+#define __EVCON_EVCON_BACKEND_H __EVCON_EVCON_BACKEND_H
+
+#include <evcon.h>
+
+typedef void (*evcon_backend_free_loop_cb)(evcon_loop *loop, void *loop_data, void *backend_data);
+
+/* fd == -1: delete watcher */
+typedef void (*evcon_backend_fd_update_cb)(evcon_fd_watcher *watcher, evcon_fd fd, int events, evcon_allocator *allocator, void *loop_data, void *watcher_data);
+
+/* special timeout values:
+ * 0: idle watcher (for background jobs)
+ * -1: disable temporarily
+ * -2: delete watcher
+ * gets called after *each* timer event to set a new timeout value
+ */
+typedef void (*evcon_backend_timer_update_cb)(evcon_timer_watcher *watcher, evcon_interval timeout, evcon_allocator *allocator, void *loop_data, void *watcher_data);
+
+typedef enum {
+ EVCON_ASYNC_TRIGGER = 0, /* <- trigger be thread safe */
+ EVCON_ASYNC_NEW = 1,
+ EVCON_ASYNC_FREE = 2
+} evcon_async_func;
+
+typedef void (*evcon_backend_async_update_cb)(evcon_async_watcher *watcher, evcon_async_func f, evcon_allocator *allocator, void *loop_data, void *watcher_data);
+
+evcon_backend* evcon_backend_new(void *backend_data,
+ evcon_allocator *allocator,
+ evcon_backend_free_loop_cb free_loop_cb,
+ evcon_backend_fd_update_cb fd_update_cb,
+ evcon_backend_timer_update_cb timer_update_cb,
+ evcon_backend_async_update_cb async_udpate_cb);
+void evcon_backend_free(evcon_backend *backend);
+
+#define EVCON_BACKEND_RECOMMENDED_SIZE (8*sizeof(void*))
+/* if memsize is large enough to contain a backend, initialize it and returns @mem. otherwise alloc a new block */
+evcon_backend* evcon_backend_init(char *mem, size_t memsize,
+ void *backend_data,
+ evcon_allocator *allocator,
+ evcon_backend_free_loop_cb free_loop_cb,
+ evcon_backend_fd_update_cb fd_update_cb,
+ evcon_backend_timer_update_cb timer_update_cb,
+ evcon_backend_async_update_cb async_udpate_cb);
+
+evcon_loop* evcon_loop_new(evcon_backend *backend, evcon_allocator *allocator);
+
+void* evcon_backend_get_data(evcon_backend *backend);
+void* evcon_loop_get_backend_data(evcon_loop *loop);
+void* evcon_fd_get_backend_data(evcon_fd_watcher *watcher);
+void* evcon_timer_get_backend_data(evcon_timer_watcher *watcher);
+void* evcon_async_get_backend_data(evcon_async_watcher *watcher);
+
+void evcon_backend_set_data(evcon_backend *backend, void *data);
+void evcon_loop_set_backend_data(evcon_loop *loop, void *data);
+void evcon_fd_set_backend_data(evcon_fd_watcher *watcher, void *data);
+void evcon_timer_set_backend_data(evcon_timer_watcher *watcher, void *data);
+void evcon_async_set_backend_data(evcon_async_watcher *watcher, void *data);
+
+
+void evcon_feed_fd(evcon_fd_watcher *watcher, int events);
+void evcon_feed_timer(evcon_timer_watcher *watcher);
+void evcon_feed_async(evcon_async_watcher *watcher);
+
+#endif
diff --git a/src/core/evcon-config-private.h.in b/src/core/evcon-config-private.h.in
new file mode 100644
index 0000000..6201f31
--- /dev/null
+++ b/src/core/evcon-config-private.h.in
@@ -0,0 +1,98 @@
+/* src/core/evcon-config-private.h.in. Generated from configure.ac by autoheader. */
+
+/* build for older glib versions even with new headers */
+#undef EVCON_GLIB_COMPAT_API
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* Define to 1 if you have the <ev.h> header file. */
+#undef HAVE_EV_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `pipe2' function. */
+#undef HAVE_PIPE2
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
diff --git a/src/core/evcon-config.h.in b/src/core/evcon-config.h.in
new file mode 100644
index 0000000..7bf42e1
--- /dev/null
+++ b/src/core/evcon-config.h.in
@@ -0,0 +1,9 @@
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
diff --git a/src/core/evcon.c b/src/core/evcon.c
new file mode 100644
index 0000000..336d56e
--- /dev/null
+++ b/src/core/evcon.c
@@ -0,0 +1,564 @@
+
+#include <evcon.h>
+#include <evcon-backend.h>
+#include <evcon-allocator.h>
+
+#include <evcon-config-private.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+
+#define EVCON_STR_LEN(s) (s), (sizeof(s)-1)
+
+struct evcon_allocator {
+ void* user_data;
+ evcon_alloc_cb alloc_cb;
+ evcon_free_cb free_cb;
+};
+
+struct evcon_backend {
+ void *backend_data;
+ evcon_allocator *allocator;
+ evcon_backend_free_loop_cb free_loop_cb;
+ evcon_backend_fd_update_cb fd_update_cb;
+ evcon_backend_timer_update_cb timer_update_cb;
+ evcon_backend_async_update_cb async_update_cb;
+};
+
+struct evcon_loop {
+ unsigned int refcount;
+ void *backend_data;
+ evcon_backend *backend;
+ evcon_allocator *allocator;
+};
+
+struct evcon_fd_watcher {
+ void *user_data;
+ void *backend_data;
+ unsigned int active:1, incallback:1, delayed_delete:1;
+ evcon_loop *loop;
+ evcon_fd_cb cb;
+ evcon_fd fd;
+ int events;
+};
+
+struct evcon_timer_watcher {
+ void *user_data;
+ void *backend_data;
+ unsigned int active:1, incallback:1, delayed_delete:1;
+ evcon_loop *loop;
+ evcon_timer_cb cb;
+ evcon_interval timeout, repeat;
+};
+
+struct evcon_async_watcher {
+ void *user_data;
+ void *backend_data;
+ unsigned int incallback:1, delayed_delete:1;
+ evcon_loop *loop;
+ evcon_async_cb cb;
+};
+
+/*****************************************************
+ * Allocator *
+ *****************************************************/
+
+void* evcon_alloc(evcon_allocator* allocator, size_t size) {
+ void *ptr;
+ if (NULL == allocator) {
+ ptr = malloc(size);
+ } else {
+ ptr = allocator->alloc_cb(size, allocator->user_data);
+ }
+ if (NULL == ptr) {
+ write(2, EVCON_STR_LEN("evcon_alloc: failed to allocate"));
+ abort();
+ }
+ return ptr;
+}
+
+void* evcon_alloc0(evcon_allocator* allocator, size_t size) {
+ void *ptr = evcon_alloc(allocator, size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void evcon_free(evcon_allocator* allocator, void* ptr, size_t size) {
+ if (NULL == ptr) return;
+ if (NULL == allocator) {
+ free(ptr);
+ } else {
+ allocator->free_cb(ptr, size, allocator->user_data);
+ }
+}
+
+evcon_allocator* evcon_allocator_new(void* user_data, evcon_alloc_cb alloc_cb, evcon_free_cb free_cb) {
+ evcon_allocator* allocator;
+
+ assert(NULL != alloc_cb);
+ assert(NULL != free_cb);
+
+ allocator = alloc_cb(sizeof(evcon_allocator), user_data);
+ allocator->user_data = user_data;
+ allocator->alloc_cb = alloc_cb;
+ allocator->free_cb = free_cb;
+
+ return allocator;
+}
+
+void evcon_allocator_free(evcon_allocator* allocator) {
+ evcon_free_cb free_cb;
+ void* user_data;
+
+ if (NULL == allocator) return;
+
+ free_cb = allocator->free_cb;
+ user_data = allocator->user_data;
+
+ memset(allocator, 0, sizeof(evcon_allocator));
+
+ free_cb(allocator, sizeof(evcon_allocator), user_data);
+}
+
+evcon_allocator* evcon_allocator_init(char *mem, size_t memsize,
+ void *user_data, evcon_alloc_cb alloc_cb, evcon_free_cb free_cb) {
+ evcon_allocator *allocator = (evcon_allocator*) mem;
+ if (sizeof(evcon_allocator) > memsize) {
+ return evcon_allocator_new(user_data, alloc_cb, free_cb);
+ } else {
+ allocator->user_data = user_data;
+ allocator->alloc_cb = alloc_cb;
+ allocator->free_cb = free_cb;
+ return allocator;
+ }
+}
+
+void* evcon_allocator_get_data(evcon_allocator *allocator) {
+ return allocator->user_data;
+}
+
+void evcon_allocator_set_data(evcon_allocator *allocator, void *user_data) {
+ allocator->user_data = user_data;
+}
+
+/*****************************************************
+ * Backend *
+ *****************************************************/
+
+evcon_backend* evcon_backend_new(void *backend_data,
+ evcon_allocator *allocator,
+ evcon_backend_free_loop_cb free_loop_cb,
+ evcon_backend_fd_update_cb fd_update_cb,
+ evcon_backend_timer_update_cb timer_update_cb,
+ evcon_backend_async_update_cb async_update_cb) {
+ evcon_backend *backend = evcon_alloc0(allocator, sizeof(evcon_backend));
+
+ backend->backend_data = backend_data;
+ backend->allocator = allocator;
+ backend->free_loop_cb = free_loop_cb;
+ backend->fd_update_cb = fd_update_cb;
+ backend->timer_update_cb = timer_update_cb;
+ backend->async_update_cb = async_update_cb;
+
+ return backend;
+}
+
+void evcon_backend_free(evcon_backend *backend) {
+ evcon_allocator *allocator;
+ if (NULL == backend) return;
+
+ allocator = backend->allocator;
+ memset(backend, 0, sizeof(evcon_backend));
+ evcon_free(allocator, backend, sizeof(evcon_backend));
+}
+
+evcon_backend* evcon_backend_init(char *mem, size_t memsize,
+ void *backend_data,
+ evcon_allocator *allocator,
+ evcon_backend_free_loop_cb free_loop_cb,
+ evcon_backend_fd_update_cb fd_update_cb,
+ evcon_backend_timer_update_cb timer_update_cb,
+ evcon_backend_async_update_cb async_update_cb) {
+ evcon_backend *backend = (evcon_backend*) mem;
+ if (sizeof(evcon_backend) > memsize) {
+ return evcon_backend_new(backend_data, allocator, free_loop_cb, fd_update_cb, timer_update_cb, async_update_cb);
+ } else {
+ backend->backend_data = backend_data;
+ backend->allocator = allocator;
+ backend->free_loop_cb = free_loop_cb;
+ backend->fd_update_cb = fd_update_cb;
+ backend->timer_update_cb = timer_update_cb;
+ backend->async_update_cb = async_update_cb;
+ }
+
+ return backend;
+}
+
+evcon_loop* evcon_loop_new(evcon_backend *backend, evcon_allocator *allocator) {
+ evcon_loop *loop;
+
+ if (NULL == allocator) allocator = backend->allocator;
+
+ loop = evcon_alloc0(allocator, sizeof(evcon_loop));
+ loop->refcount = 1;
+ loop->allocator = allocator;
+ loop->backend = backend;
+
+ return loop;
+}
+
+void* evcon_backend_get_data(evcon_backend *backend) {
+ return backend->backend_data;
+}
+void* evcon_loop_get_backend_data(evcon_loop *loop) {
+ return loop->backend_data;
+}
+void* evcon_fd_get_backend_data(evcon_fd_watcher *watcher) {
+ return watcher->backend_data;
+}
+void* evcon_timer_get_backend_data(evcon_timer_watcher *watcher) {
+ return watcher->backend_data;
+}
+void* evcon_async_get_backend_data(evcon_async_watcher *watcher) {
+ return watcher->backend_data;
+}
+
+void evcon_backend_set_data(evcon_backend *backend, void *data) {
+ backend->backend_data = data;
+}
+void evcon_loop_set_backend_data(evcon_loop *loop, void *data) {
+ loop->backend_data = data;
+}
+void evcon_fd_set_backend_data(evcon_fd_watcher *watcher, void *data) {
+ watcher->backend_data = data;
+}
+void evcon_timer_set_backend_data(evcon_timer_watcher *watcher, void *data) {
+ watcher->backend_data = data;
+}
+void evcon_async_set_backend_data(evcon_async_watcher *watcher, void *data) {
+ watcher->backend_data = data;
+}
+
+static void evcon_backend_fd_update(evcon_fd_watcher *watcher) {
+ evcon_backend *backend = watcher->loop->backend;
+ int fd = watcher->fd, events = watcher->events;
+ if (!watcher->active || -1 == fd) events = 0;
+ backend->fd_update_cb(watcher, fd, events, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+}
+
+/* this restarts an active timer! */
+static void evcon_backend_timer_update(evcon_timer_watcher *watcher) {
+ evcon_backend *backend = watcher->loop->backend;
+ evcon_interval timeout = watcher->timeout;
+ if (!watcher->active || timeout < 0) timeout = -1;
+ backend->timer_update_cb(watcher, timeout, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+}
+/* tell backend to delete timer */
+static void evcon_backend_timer_delete(evcon_timer_watcher *watcher) {
+ evcon_backend *backend = watcher->loop->backend;
+ backend->timer_update_cb(watcher, -2, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+}
+
+void evcon_feed_fd(evcon_fd_watcher *watcher, int events) {
+ int oldfd, oldevents;
+ if (watcher->incallback) return;
+
+ oldfd = watcher->fd;
+ oldevents = watcher->events;
+
+ watcher->incallback = 1;
+ watcher->cb(watcher->loop, watcher, oldfd, events, watcher->user_data);
+ watcher->incallback = 0;
+
+ if (watcher->delayed_delete) {
+ evcon_fd_free(watcher);
+ return;
+ }
+
+ if (oldfd != watcher->fd || oldevents != watcher->events) evcon_backend_fd_update(watcher);
+}
+
+void evcon_feed_timer(evcon_timer_watcher *watcher) {
+ if (watcher->incallback) return;
+ watcher->timeout = watcher->repeat;
+
+ watcher->incallback = 1;
+ watcher->cb(watcher->loop, watcher, watcher->user_data);
+ watcher->incallback = 0;
+
+ if (watcher->delayed_delete) {
+ evcon_timer_free(watcher);
+ return;
+ }
+
+ evcon_backend_timer_update(watcher);
+}
+
+void evcon_feed_async(evcon_async_watcher *watcher) {
+ if (watcher->incallback) return;
+
+ watcher->incallback = 1;
+ watcher->cb(watcher->loop, watcher, watcher->user_data);
+ watcher->incallback = 0;
+
+ if (watcher->delayed_delete) {
+ evcon_async_free(watcher);
+ return;
+ }
+}
+
+/*****************************************************
+ * Main interface *
+ *****************************************************/
+
+void evcon_loop_ref(evcon_loop *loop) {
+ assert(loop->refcount > 0);
+ ++loop->refcount;
+}
+void evcon_loop_unref(evcon_loop *loop) {
+ if (!loop) return;
+ assert(loop->refcount > 0);
+
+ if (0 == --(loop->refcount)) {
+ evcon_allocator *allocator = loop->allocator;
+
+ loop->refcount = 1; /* fake reference: allows loops to use own watchers with weak references */
+ loop->backend->free_loop_cb(loop, loop->backend_data, loop->backend->backend_data);
+ memset(loop, 0, sizeof(evcon_loop));
+ evcon_free(allocator, loop, sizeof(evcon_loop));
+ }
+}
+
+evcon_allocator* evcon_loop_get_allocator(evcon_loop *loop) {
+ return loop->allocator;
+}
+
+void evcon_init_fd(evcon_fd fd) {
+#ifdef FD_CLOEXEC
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+#ifdef O_NONBLOCK
+ fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
+#elif defined _WIN32
+ int i = 1;
+ ioctlsocket(fd, FIONBIO, &i);
+#else
+#error No way found to set non-blocking mode for fds.
+#endif
+}
+
+evcon_fd_watcher* evcon_fd_new(evcon_loop *loop, evcon_fd_cb cb, evcon_fd fd, int events, void* user_data) {
+ evcon_fd_watcher *watcher = evcon_alloc0(loop->allocator, sizeof(evcon_fd_watcher));
+ evcon_loop_ref(loop);
+
+ watcher->user_data = user_data;
+ watcher->backend_data = NULL;
+ watcher->active = watcher->incallback = watcher->delayed_delete = 0;
+ watcher->loop = loop;
+ watcher->cb = cb;
+ watcher->fd = fd;
+ watcher->events = events;
+
+ return watcher;
+}
+
+void evcon_fd_start(evcon_fd_watcher *watcher) {
+ if (!watcher->active) {
+ watcher->active = 1;
+ evcon_backend_fd_update(watcher);
+ }
+}
+
+void evcon_fd_stop(evcon_fd_watcher *watcher) {
+ if (watcher->active) {
+ watcher->active = 0;
+ evcon_backend_fd_update(watcher);
+ }
+}
+
+void evcon_fd_free(evcon_fd_watcher* watcher) {
+ watcher->active = 0;
+ watcher->fd = -1;
+ watcher->events = 0;
+ if (watcher->incallback) { /* delay delete */
+ watcher->delayed_delete = 1;
+ } else {
+ evcon_loop *loop = watcher->loop;
+ evcon_backend_fd_update(watcher);
+ memset(watcher, 0, sizeof(evcon_fd_watcher));
+ evcon_free(loop->allocator, watcher, sizeof(evcon_fd_watcher));
+ evcon_loop_unref(loop);
+ }
+}
+
+int evcon_fd_is_active(evcon_fd_watcher* watcher) {
+ return watcher->active;
+}
+
+evcon_fd_cb evcon_fd_get_cb(evcon_fd_watcher *watcher) {
+ return watcher->cb;
+}
+evcon_fd evcon_fd_get_fd(evcon_fd_watcher *watcher) {
+ return watcher->fd;
+}
+int evcon_fd_get_events(evcon_fd_watcher *watcher) {
+ return watcher->events;
+}
+void* evcon_fd_get_user_data(evcon_fd_watcher *watcher) {
+ return watcher->user_data;
+}
+evcon_loop *evcon_fd_get_loop(evcon_fd_watcher *watcher) {
+ return watcher->loop;
+}
+
+void evcon_fd_set_cb(evcon_fd_watcher *watcher, evcon_fd_cb cb) {
+ watcher->cb = cb;
+}
+void evcon_fd_set_fd(evcon_fd_watcher *watcher, evcon_fd fd) {
+ watcher->fd = fd;
+ if (watcher->active && !watcher->incallback) evcon_backend_fd_update(watcher);
+}
+void evcon_fd_set_events(evcon_fd_watcher *watcher, int events) {
+ watcher->events = events;
+ if (-1 != watcher->fd && watcher->active && !watcher->incallback) evcon_backend_fd_update(watcher);
+}
+void evcon_fd_set_user_data(evcon_fd_watcher *watcher, void* user_data) {
+ watcher->user_data = user_data;
+}
+
+evcon_timer_watcher *evcon_timer_new(evcon_loop *loop, evcon_timer_cb cb, void *user_data) {
+ evcon_timer_watcher *watcher = evcon_alloc0(loop->allocator, sizeof(evcon_timer_watcher));
+ evcon_loop_ref(loop);
+
+ watcher->user_data = user_data;
+ watcher->backend_data = NULL;
+ watcher->active = watcher->incallback = watcher->delayed_delete = 0;
+ watcher->loop = loop;
+ watcher->cb = cb;
+ watcher->timeout = -1;
+ watcher->repeat = -1;
+
+ return watcher;
+}
+
+void evcon_timer_once(evcon_timer_watcher *watcher, evcon_interval timeout) {
+ watcher->timeout = timeout;
+ watcher->repeat = -1;
+ watcher->active = 1;
+ if (!watcher->incallback) evcon_backend_timer_update(watcher);
+}
+
+void evcon_timer_repeat(evcon_timer_watcher *watcher, evcon_interval repeat) {
+ watcher->timeout = repeat;
+ watcher->repeat = repeat;
+ watcher->active = 1;
+ if (!watcher->incallback) evcon_backend_timer_update(watcher);
+}
+
+void evcon_timer_stop(evcon_timer_watcher *watcher) {
+ if (watcher->active) {
+ watcher->active = 0;
+ evcon_backend_timer_update(watcher);
+ }
+}
+
+void evcon_timer_free(evcon_timer_watcher *watcher) {
+ watcher->active = 0;
+ watcher->timeout = watcher->repeat = -1;
+ if (watcher->incallback) { /* delay delete */
+ watcher->delayed_delete = 1;
+ } else {
+ evcon_loop *loop = watcher->loop;
+ evcon_backend_timer_delete(watcher);
+ memset(watcher, 0, sizeof(evcon_timer_watcher));
+ evcon_free(loop->allocator, watcher, sizeof(evcon_timer_watcher));
+ evcon_loop_unref(loop);
+ }
+}
+
+int evcon_timer_is_active(evcon_fd_watcher* watcher) {
+ return watcher->active;
+}
+
+evcon_timer_cb evcon_timer_get_cb(evcon_timer_watcher *watcher) {
+ return watcher->cb;
+}
+evcon_interval evcon_timer_get_timeout(evcon_timer_watcher *watcher) {
+ return watcher->timeout;
+}
+evcon_interval evcon_timer_get_repeat(evcon_timer_watcher *watcher) {
+ return watcher->repeat;
+}
+void* evcon_timer_get_user_data(evcon_timer_watcher *watcher) {
+ return watcher->user_data;
+}
+evcon_loop *evcon_timer_get_loop(evcon_timer_watcher *watcher) {
+ return watcher->loop;
+}
+
+void evcon_timer_set_cb(evcon_timer_watcher *watcher, evcon_timer_cb cb) {
+ watcher->cb = cb;
+}
+void evcon_timer_set_repeat(evcon_timer_watcher *watcher, evcon_interval repeat) {
+ watcher->repeat = repeat;
+}
+void evcon_timer_set_user_data(evcon_timer_watcher *watcher, void *user_data) {
+ watcher->user_data = user_data;
+}
+
+evcon_async_watcher* evcon_async_new(evcon_loop *loop, evcon_async_cb cb, void* user_data) {
+ evcon_async_watcher *watcher = evcon_alloc0(loop->allocator, sizeof(evcon_async_watcher));
+ evcon_backend *backend = watcher->loop->backend;
+ evcon_loop_ref(loop);
+
+ watcher->user_data = user_data;
+ watcher->backend_data = NULL;
+ watcher->incallback = watcher->delayed_delete = 0;
+ watcher->loop = loop;
+ watcher->cb = cb;
+
+ backend->async_update_cb(watcher, EVCON_ASYNC_NEW, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+ return watcher;
+}
+
+void evcon_async_wakeup(evcon_async_watcher *watcher) {
+ evcon_backend *backend = watcher->loop->backend;
+ backend->async_update_cb(watcher, EVCON_ASYNC_TRIGGER, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+}
+
+void evcon_async_free(evcon_async_watcher* watcher) {
+ if (watcher->incallback) { /* delay delete */
+ watcher->delayed_delete = 1;
+ } else {
+ evcon_loop *loop = watcher->loop;
+ evcon_backend *backend = watcher->loop->backend;
+
+ backend->async_update_cb(watcher, EVCON_ASYNC_FREE, watcher->loop->allocator, watcher->loop->backend_data, watcher->backend_data);
+
+ memset(watcher, 0, sizeof(evcon_async_watcher));
+ evcon_free(loop->allocator, watcher, sizeof(evcon_async_watcher));
+ evcon_loop_unref(loop);
+ }
+}
+
+evcon_async_cb evcon_async_get_cb(evcon_async_watcher *watcher) {
+ return watcher->cb;
+}
+void* evcon_async_get_user_data(evcon_async_watcher *watcher) {
+ return watcher->user_data;
+}
+evcon_loop *evcon_async_get_loop(evcon_async_watcher *watcher) {
+ return watcher->loop;
+}
+
+void evcon_async_set_cb(evcon_async_watcher *watcher, evcon_async_cb cb) {
+ watcher->cb = cb;
+}
+void evcon_async_set_user_data(evcon_async_watcher *watcher, void* user_data) {
+ watcher->user_data = user_data;
+}
diff --git a/src/core/evcon.h b/src/core/evcon.h
new file mode 100644
index 0000000..0e0c9e0
--- /dev/null
+++ b/src/core/evcon.h
@@ -0,0 +1,114 @@
+#ifndef __EVCON_EVCON_H
+#define __EVCON_EVCON_H __EVCON_EVCON_H
+
+#include <evcon-config.h>
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+/* (positive) time interval in milliseconds; negative values have special meanings */
+/* use of macros is recommended, in case someone needs to change this for porting */
+
+typedef int64_t evcon_interval;
+#define EVCON_INTERVAL_FROM_DOUBLE_SEC(x) ((evcon_interval) ceil((x)*1.e3))
+#define EVCON_INTERVAL_FROM_NSEC(x) ((x+(evcon_interval)1e6-1)/1e6)
+#define EVCON_INTERVAL_FROM_USEC(x) ((x+(evcon_interval)1e3-1)/1e3)
+#define EVCON_INTERVAL_FROM_MSEC(x) (x)
+#define EVCON_INTERVAL_FROM_SEC(x) ((x)*(evcon_interval)1e3)
+
+#define EVCON_INTERVAL_AS_DOUBLE_SEC(x) ((x)*1.e-3)
+#define EVCON_INTERVAL_AS_NSEC(x) ((x)*1e6)
+#define EVCON_INTERVAL_AS_USEC(x) ((x)*1e3)
+#define EVCON_INTERVAL_AS_MSEC(x) (x)
+#define EVCON_INTERVAL_AS_SEC(x) ((x+1e3-1)/1e3)
+
+
+typedef struct evcon_loop evcon_loop;
+typedef struct evcon_backend evcon_backend;
+typedef struct evcon_allocator evcon_allocator;
+
+typedef struct evcon_fd_watcher evcon_fd_watcher;
+typedef struct evcon_timer_watcher evcon_timer_watcher;
+typedef struct evcon_async_watcher evcon_async_watcher;
+
+typedef int evcon_fd;
+
+typedef enum {
+ EVCON_ERROR = 0x0001,
+ EVCON_READ = 0x0002,
+ EVCON_WRITE = 0x0004
+} evcon_events;
+
+typedef void (*evcon_fd_cb)(evcon_loop *loop, evcon_fd_watcher *watcher, evcon_fd fd, int revents, void* user_data);
+typedef void (*evcon_timer_cb)(evcon_loop *loop, evcon_timer_watcher *watcher, void* user_data);
+typedef void (*evcon_async_cb)(evcon_loop *loop, evcon_async_watcher *watcher, void* user_data);
+
+/* each watcher keeps a reference; only backends are allowed to
+ * "undo" the reference count for internal watchers (see glib-backend.c for an example)
+ */
+void evcon_loop_ref(evcon_loop *loop);
+void evcon_loop_unref(evcon_loop *loop);
+
+evcon_allocator* evcon_loop_get_allocator(evcon_loop *loop);
+
+/* sets fd to non-blocking (and FD_CLOEXEC if supported) */
+void evcon_init_fd(evcon_fd fd);
+
+/* fd watcher */
+evcon_fd_watcher* evcon_fd_new(evcon_loop *loop, evcon_fd_cb cb, evcon_fd fd, int events, void* user_data);
+void evcon_fd_start(evcon_fd_watcher *watcher);
+void evcon_fd_stop(evcon_fd_watcher *watcher);
+void evcon_fd_free(evcon_fd_watcher* watcher);
+int evcon_fd_is_active(evcon_fd_watcher* watcher); /* 1 == started, 0 == stopped */
+
+evcon_fd_cb evcon_fd_get_cb(evcon_fd_watcher *watcher);
+evcon_fd evcon_fd_get_fd(evcon_fd_watcher *watcher);
+int evcon_fd_get_events(evcon_fd_watcher *watcher);
+void* evcon_fd_get_user_data(evcon_fd_watcher *watcher);
+evcon_loop *evcon_fd_get_loop(evcon_fd_watcher *watcher);
+
+void evcon_fd_set_cb(evcon_fd_watcher *watcher, evcon_fd_cb cb);
+void evcon_fd_set_fd(evcon_fd_watcher *watcher, evcon_fd fd);
+void evcon_fd_set_events(evcon_fd_watcher *watcher, int events);
+void evcon_fd_set_user_data(evcon_fd_watcher *watcher, void* user_data);
+
+/* timer watcher. all times are relative, < 0 means "disabled" */
+evcon_timer_watcher *evcon_timer_new(evcon_loop *loop, evcon_timer_cb cb, void *user_data);
+void evcon_timer_once(evcon_timer_watcher *watcher, evcon_interval timeout); /* (re)start timer; triggering in @timeout seconds, then stop (sets repeat = -1) */
+void evcon_timer_repeat(evcon_timer_watcher *watcher, evcon_interval repeat); /* (re)start timer; triggering in @timeout seconds, then start again */
+void evcon_timer_stop(evcon_timer_watcher *watcher);
+void evcon_timer_free(evcon_timer_watcher *watcher);
+int evcon_timer_is_active(evcon_fd_watcher* watcher); /* 1 == started, 0 == stopped */
+
+evcon_timer_cb evcon_timer_get_cb(evcon_timer_watcher *watcher);
+evcon_interval evcon_timer_get_timeout(evcon_timer_watcher *watcher); /* last used timeout, not the time until next event. after a trigger this gets setted to the repeat value */
+evcon_interval evcon_timer_get_repeat(evcon_timer_watcher *watcher);
+void* evcon_timer_get_user_data(evcon_timer_watcher *watcher);
+evcon_loop *evcon_timer_get_loop(evcon_timer_watcher *watcher);
+
+void evcon_timer_set_cb(evcon_timer_watcher *watcher, evcon_timer_cb cb);
+void evcon_timer_set_repeat(evcon_timer_watcher *watcher, evcon_interval repeat); /* set repeat value for the future, doesn't change current timer nor does it start the watcher */
+void evcon_timer_set_user_data(evcon_timer_watcher *watcher, void *user_data);
+
+/* async watcher. */
+evcon_async_watcher *evcon_async_new(evcon_loop *loop, evcon_async_cb cb, void *user_data);
+void evcon_async_wakeup(evcon_async_watcher *watcher); /* thread-safe */
+void evcon_async_free(evcon_async_watcher *watcher);
+
+evcon_async_cb evcon_async_get_cb(evcon_async_watcher *watcher);
+void* evcon_async_get_user_data(evcon_async_watcher *watcher);
+evcon_loop *evcon_async_get_loop(evcon_async_watcher *watcher);
+
+void evcon_async_set_cb(evcon_async_watcher *watcher, evcon_async_cb cb);
+void evcon_async_set_user_data(evcon_async_watcher *watcher, void *user_data);
+
+#endif
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644
index 0000000..afc79ea
--- /dev/null
+++ b/src/tests/Makefile.am
@@ -0,0 +1,33 @@
+
+AM_CFLAGS = -I$(srcdir)/../core -I$(srcdir)/../backend-ev -I$(srcdir)/../backend-glib -I$(srcdir)/../backend-event
+AM_CFLAGS += $(GLIB_CFLAGS) $(LIBEV_CFLAGS) $(LIBEVENT_CFLAGS)
+
+test_binaries =
+
+if BUILD_EV
+test_binaries += evcon-test-ev
+evcon_test_ev_SOURCES = evcon-test-ev.c evcon-echo.c
+evcon_test_ev_LDFLAGS = -export-dynamic -avoid-version -no-undefined $(GLIB_LIBS) $(LIBEV_LIBS)
+evcon_test_ev_LDADD = ../backend-ev/libevcon-ev.la ../backend-glib/libevcon-glib.la ../core/libevcon.la
+endif
+
+if BUILD_GLIB
+test_binaries += evcon-test-glib
+evcon_test_glib_SOURCES = evcon-test-glib.c evcon-echo.c
+evcon_test_glib_LDFLAGS = -export-dynamic -avoid-version -no-undefined $(GLIB_LIBS)
+evcon_test_glib_LDADD = ../backend-glib/libevcon-glib.la ../core/libevcon.la
+endif
+
+if BUILD_EVENT
+test_binaries += evcon-test-event
+evcon_test_event_SOURCES = evcon-test-event.c evcon-echo.c
+evcon_test_event_LDFLAGS = -export-dynamic -avoid-version -no-undefined $(LIBEVENT_LIBS)
+evcon_test_event_LDADD = ../backend-event/libevcon-event.la ../core/libevcon.la
+endif
+
+EXTRA_DIST = evcon-echo.h
+
+check_PROGRAMS=$(test_binaries)
+
+TESTS=$(test_binaries)
+TESTS_ENVIRONMENT=gtester --verbose
diff --git a/src/tests/evcon-echo.c b/src/tests/evcon-echo.c
new file mode 100644
index 0000000..150e3a4
--- /dev/null
+++ b/src/tests/evcon-echo.c
@@ -0,0 +1,365 @@
+
+#include "evcon-echo.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define UNUSED(x) ((void)(x))
+
+static void echo_server_con_cb(evcon_loop *loop, evcon_fd_watcher *watcher, evcon_fd fd, int revents, void* user_data) {
+ static char buf[512];
+ EchoServerConnection *con = (EchoServerConnection*) user_data;
+ int r, r1;
+ UNUSED(loop);
+ UNUSED(watcher);
+ UNUSED(revents);
+
+ r = read(fd, buf, sizeof(buf));
+
+ if (-1 == r) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return;
+ default:
+ g_warning("Connection error (fatal): %s\n", g_strerror(errno));
+ goto closecon;
+ }
+ }
+ if (0 == r) goto closecon;
+
+ r1 = write(fd, buf, r);
+
+ if (-1 == r1) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ g_warning("Connection: write failed (temporary error), loosing buffer: %s\n", g_strerror(errno));
+ return;
+ default:
+ g_warning("Connection error (fatal): %s\n", g_strerror(errno));
+ goto closecon;
+ }
+ }
+
+ if (r1 < r) {
+ g_warning("Connection: write not complete, loosing remaining data\n");
+ }
+
+ return;
+
+closecon:
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
+ evcon_fd_free(con->conn_watcher);
+ g_queue_unlink(&con->srv->connections, &con->con_link);
+ g_slice_free(EchoServerConnection, con);
+}
+
+static EchoServerConnection* echo_server_con_new(EchoServer* srv, evcon_fd fd) {
+ EchoServerConnection *con = g_slice_new0(EchoServerConnection);
+ evcon_init_fd(fd);
+ con->srv = srv;
+ con->conn_watcher = evcon_fd_new(srv->loop, echo_server_con_cb, fd, EVCON_READ, con);
+ evcon_fd_start(con->conn_watcher);
+ con->con_link.data = con;
+ g_queue_push_tail_link(&srv->connections, &con->con_link);
+ return con;
+}
+
+static void echo_server_listen_cb(evcon_loop *loop, evcon_fd_watcher *watcher, evcon_fd fd, int revents, void* user_data) {
+ EchoServer *srv = (EchoServer*) user_data;
+ int confd;
+ UNUSED(loop);
+ UNUSED(watcher);
+ UNUSED(revents);
+
+ while (-1 != (confd = accept(fd, NULL, NULL))) {
+ echo_server_con_new(srv, confd);
+ }
+
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return;
+ default:
+ g_error("accept() failed: %s\n", g_strerror(errno));
+ break;
+ }
+}
+
+EchoServer* echo_server_new(evcon_loop *loop) {
+ EchoServer *srv = g_slice_new0(EchoServer);
+ struct sockaddr_in addr;
+ int fd;
+
+ evcon_loop_ref(loop);
+ srv->loop = loop;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ evcon_init_fd(fd);
+ if (-1 == fd) g_error("socket() failed: %s\n", g_strerror(errno));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ if (-1 == bind(fd, (struct sockaddr*) &addr, sizeof(addr))) g_error("bind() failed: %s\n", g_strerror(errno));
+
+ if (-1 == listen(fd, 128)) g_error("listen() failed: %s\n", g_strerror(errno));
+
+ {
+ socklen_t addrlen = sizeof(addr);
+ if (-1 == getsockname(fd, (struct sockaddr*) &addr, &addrlen)) g_error("getsockname() failed: %s\n", g_strerror(errno));
+ srv->port = ntohs(addr.sin_port);
+ }
+
+ srv->listen_watcher = evcon_fd_new(srv->loop, echo_server_listen_cb, fd, EVCON_READ, srv);
+ evcon_fd_start(srv->listen_watcher);
+
+ return srv;
+}
+
+void echo_server_free(EchoServer *srv) {
+ GList *link;
+
+ while (NULL != (link = g_queue_pop_head_link(&srv->connections))) {
+ EchoServerConnection *con = link->data;
+ int fd = evcon_fd_get_fd(con->conn_watcher);
+
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
+ evcon_fd_free(con->conn_watcher);
+ g_slice_free(EchoServerConnection, con);
+ }
+
+ close(evcon_fd_get_fd(srv->listen_watcher));
+ evcon_fd_free(srv->listen_watcher);
+
+ evcon_loop_unref(srv->loop);
+ g_slice_free(EchoServer, srv);
+}
+
+#define ECHO_CLIENT_TIMOUT_MSEC (250)
+
+static void echo_client_con_timer_once(EchoClientConnection *con) {
+ clock_gettime(CLOCK_MONOTONIC, &con->timer_start);
+ evcon_timer_once(con->timout_watcher, EVCON_INTERVAL_FROM_MSEC(ECHO_CLIENT_TIMOUT_MSEC));
+}
+
+static void echo_client_con_check_timeout(EchoClientConnection *con) {
+ struct timespec ts;
+ int msecs;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ msecs = 1000*(ts.tv_sec - con->timer_start.tv_sec) + (ts.tv_nsec - con->timer_start.tv_nsec + 999999)/1000000;
+
+ g_debug("timout triggered after %ims", msecs);
+
+ if (msecs > ECHO_CLIENT_TIMOUT_MSEC + 50) {
+ if (msecs > ECHO_CLIENT_TIMOUT_MSEC + 2000) {
+ g_error("timeout triggered more than 2s too late (after %ims)", msecs);
+ } else {
+ g_message("timeout triggered more than 50ms too late (after %ims)", msecs);
+ }
+ } else if (msecs < ECHO_CLIENT_TIMOUT_MSEC - 5) {
+ g_error("timeout triggered more than 5ms too early (after %ims)", msecs);
+ }
+}
+
+static void echo_client_fd_cb(evcon_loop *loop, evcon_fd_watcher *watcher, evcon_fd fd, int revents, void* user_data) {
+ EchoClientConnection *con = (EchoClientConnection*) user_data;
+ EchoClient *client = con->client;
+ int r;
+ UNUSED(loop);
+ UNUSED(watcher);
+
+ g_debug("fd %i cb: read=%i, wrote=%i, data size=%i, closing=%i, connected=%i\n", fd, con->did_read, con->did_write, (int) sizeof(con->data), con->closing, con->connected);
+
+ if (!con->connected) {
+ struct sockaddr addr;
+ socklen_t len;
+
+ len = sizeof(addr);
+ if (-1 == getpeername(fd, &addr, &len)) {
+ /* connect failed; find out why */
+ int err;
+ len = sizeof(err);
+#ifdef SO_ERROR
+ getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&err, &len);
+#else
+ {
+ char ch;
+ errno = 0;
+ read(fd, &ch, 1);
+ err = errno;
+ }
+#endif
+ g_error("Couldn't connect: %s\n", g_strerror(errno));
+ }
+
+ con->connected = TRUE;
+ echo_client_con_timer_once(con);
+ revents = EVCON_WRITE;
+ }
+
+ if (0 != (EVCON_WRITE & revents)) {
+ if (con->did_write < sizeof(con->data)) {
+ r = write(fd, con->data + con->did_write, sizeof(con->data) - con->did_write);
+ if (0 > r) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ break;
+ default:
+ g_error("write() failed: %s\n", g_strerror(errno));
+ }
+ } else {
+ con->did_write += r;
+ }
+ } else {
+ g_warning("got write event for fd %i we didn't want", fd);
+ }
+ if (con->did_write == sizeof(con->data)) {
+ evcon_fd_set_events(con->conn_watcher, EVCON_READ);
+ }
+ }
+
+ if (0 != (EVCON_READ & revents)) {
+ char buf[256];
+
+ r = read(fd, buf, sizeof(buf));
+ if (-1 == r) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ break;
+ default:
+ g_error("read() failed: %s\n", g_strerror(errno));
+ }
+ } else if (0 == r) {
+ if (!con->closing) g_error("unexpected EOF");
+
+ close(fd);
+ evcon_fd_free(con->conn_watcher);
+ g_queue_unlink(&client->connections, &con->con_link);
+ g_slice_free(EchoClientConnection, con);
+ if (0 == client->connections.length) {
+ client->finished_cb(client, client->finished_data);
+ }
+ return;
+ } else {
+ if (r + con->did_read > con->did_write) g_error("received more than we sent");
+ if (0 != memcmp(con->data + con->did_read, buf, r)) g_error("received non matching data");
+ con->did_read += r;
+ }
+ }
+}
+
+static void echo_client_timeout_cb(evcon_loop *loop, evcon_timer_watcher *watcher, void* user_data) {
+ EchoClientConnection *con = (EchoClientConnection*) user_data;
+ UNUSED(loop);
+ UNUSED(watcher);
+
+ echo_client_con_check_timeout(con);
+
+ if (!con->connected) {
+ g_error("didn't connect before timeout");
+ }
+ if (!con->closing) {
+ if (con->did_write < sizeof(con->data)) {
+ g_error("didn't write all data before timeout");
+ }
+ if (con->did_read < con->did_write) {
+ g_error("didn't receive all data back before timeout");
+ }
+ con->closing = TRUE;
+ shutdown(evcon_fd_get_fd(con->conn_watcher), SHUT_WR);
+ } else {
+ g_error("server didn't close connection before timeout");
+ }
+}
+
+EchoClient* echo_client_new(EchoServer *srv, int count, EchoClientFinishedCB finished_cb, void *finished_data) {
+ EchoClient* client = g_slice_new0(EchoClient);
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = htons(srv->port);
+
+ client->finished_cb = finished_cb;
+ client->finished_data = finished_data;
+
+ client->srv = srv;
+ evcon_loop_ref(srv->loop);
+ client->loop = srv->loop;
+
+ for (int i = 0; i < count; ++i) {
+ EchoClientConnection *con = g_slice_new0(EchoClientConnection);
+ int fd;
+ con->client = client;
+ for (guint j = 0; j < sizeof(con->data); ++j) con->data[j] = 'A' + (i << 4) + j;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (-1 == fd) g_error("socket() failed: %s\n", g_strerror(errno));
+ evcon_init_fd(fd);
+ con->connected = FALSE;
+ if (-1 == connect(fd, (struct sockaddr*) &addr, sizeof(addr))) {
+ switch (errno) {
+ case EINPROGRESS:
+ case EALREADY:
+ case EINTR:
+ break;
+ default:
+ g_error("connect() failed: %s\n", g_strerror(errno));
+ }
+ } else {
+ con->connected = TRUE;
+ }
+ con->conn_watcher = evcon_fd_new(client->loop, echo_client_fd_cb, fd, EVCON_READ | EVCON_WRITE, con);
+ evcon_fd_start(con->conn_watcher);
+ con->timout_watcher = evcon_timer_new(client->loop, echo_client_timeout_cb, con);
+ echo_client_con_timer_once(con);
+
+ con->con_link.data = con;
+ g_queue_push_tail_link(&client->connections, &con->con_link);
+ }
+
+ return client;
+}
+
+void echo_client_free(EchoClient *client) {
+ GList *list;
+
+ while (NULL != (list = g_queue_pop_head_link(&client->connections))) {
+ EchoClientConnection *con = (EchoClientConnection*) list->data;
+
+ close(evcon_fd_get_fd(con->conn_watcher));
+ evcon_fd_free(con->conn_watcher);
+ evcon_timer_free(con->timout_watcher);
+ g_slice_free(EchoClientConnection, con);
+ }
+
+ evcon_loop_unref(client->loop);
+ g_slice_free(EchoClient, client);
+}
diff --git a/src/tests/evcon-echo.h b/src/tests/evcon-echo.h
new file mode 100644
index 0000000..49a5beb
--- /dev/null
+++ b/src/tests/evcon-echo.h
@@ -0,0 +1,54 @@
+#ifndef __EVCON_ECHO_H
+#define __EVCON_ECHO_H __EVCON_ECHO_H
+
+#include <evcon.h>
+#include <glib.h>
+
+#include <time.h>
+
+typedef struct EchoServerConnection EchoServerConnection;
+typedef struct EchoServer EchoServer;
+
+struct EchoServerConnection {
+ EchoServer *srv;
+ evcon_fd_watcher *conn_watcher;
+ GList con_link;
+};
+struct EchoServer {
+ unsigned short port;
+ evcon_loop *loop;
+ evcon_fd_watcher *listen_watcher;
+ GQueue connections; /* <EchoServerConnection> */
+};
+
+EchoServer* echo_server_new(evcon_loop *loop);
+void echo_server_free(EchoServer *srv);
+
+
+typedef struct EchoClientConnection EchoClientConnection;
+typedef struct EchoClient EchoClient;
+typedef void (*EchoClientFinishedCB)(EchoClient* client, void *user_data);
+
+struct EchoClientConnection {
+ EchoClient *client;
+ evcon_fd_watcher *conn_watcher;
+ evcon_timer_watcher *timout_watcher;
+ struct timespec timer_start;
+ GList con_link;
+ gboolean connected, closing;
+ char data[128];
+ guint did_read, did_write;
+};
+struct EchoClient {
+ EchoServer *srv;
+ evcon_loop *loop;
+ GQueue connections; /* <EchoClientConnection> */
+
+ EchoClientFinishedCB finished_cb;
+ void *finished_data;
+};
+
+EchoClient* echo_client_new(EchoServer *srv, int count, EchoClientFinishedCB finished_cb, void *finished_data);
+void echo_client_free(EchoClient *client);
+
+#endif
diff --git a/src/tests/evcon-test-ev.c b/src/tests/evcon-test-ev.c
new file mode 100644
index 0000000..c17fb28
--- /dev/null
+++ b/src/tests/evcon-test-ev.c
@@ -0,0 +1,45 @@
+
+#include "evcon-echo.h"
+#include <evcon-ev.h>
+#include <evcon-glib.h>
+
+#define UNUSED(x) ((void)(x))
+
+static void test_ev_client_finished_cb(EchoClient* client, void *user_data) {
+ struct ev_loop *loop = (struct ev_loop*) user_data;
+ UNUSED(client);
+
+ ev_break(loop, EVBREAK_ONE);
+}
+
+
+static void test_ev(void) {
+ struct ev_loop *l = ev_loop_new(0);
+ evcon_allocator *alloc = evcon_glib_allocator();
+ evcon_loop *loop = evcon_loop_from_ev(l, alloc);
+ EchoClient *client;
+ EchoServer *srv;
+
+ srv = echo_server_new(loop);
+ g_debug("Listening on port %i\n", srv->port);
+
+ client = echo_client_new(srv, 5, test_ev_client_finished_cb, l);
+
+ ev_run(l, 0);
+
+ echo_client_free(client);
+ echo_server_free(srv);
+
+ evcon_loop_unref(loop);
+
+ ev_loop_destroy(l);
+}
+
+
+int main(int argc, char** argv) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/evcon-echo/test-ev", test_ev);
+
+ return g_test_run();
+}
diff --git a/src/tests/evcon-test-event.c b/src/tests/evcon-test-event.c
new file mode 100644
index 0000000..aa298ad
--- /dev/null
+++ b/src/tests/evcon-test-event.c
@@ -0,0 +1,43 @@
+
+#include "evcon-echo.h"
+
+#include <evcon-event.h>
+
+#define UNUSED(x) ((void)(x))
+
+static void test_event_client_finished_cb(EchoClient* client, void *user_data) {
+ struct event_base *base = (struct event_base*) user_data;
+ UNUSED(client);
+
+ event_base_loopbreak(base);
+}
+
+
+static void test_event(void) {
+ struct event_base *base = event_base_new();
+ evcon_loop *loop = evcon_loop_from_event(base, NULL);
+ EchoClient *client;
+ EchoServer *srv;
+
+ srv = echo_server_new(loop);
+ g_debug("Listening on port %i\n", srv->port);
+
+ client = echo_client_new(srv, 5, test_event_client_finished_cb, base);
+
+ event_base_dispatch(base);
+
+ echo_client_free(client);
+ echo_server_free(srv);
+
+ evcon_loop_unref(loop);
+
+ event_base_free(base);
+}
+
+int main(int argc, char** argv) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/evcon-echo/test-event", test_event);
+
+ return g_test_run();
+}
diff --git a/src/tests/evcon-test-glib.c b/src/tests/evcon-test-glib.c
new file mode 100644
index 0000000..36a12e8
--- /dev/null
+++ b/src/tests/evcon-test-glib.c
@@ -0,0 +1,47 @@
+
+#include "evcon-echo.h"
+#include <evcon-glib.h>
+
+#define UNUSED(x) ((void)(x))
+
+static void test_glib_client_finished_cb(EchoClient* client, void *user_data) {
+ GMainLoop *loop = (GMainLoop*) user_data;
+ UNUSED(client);
+
+ g_main_loop_quit(loop);
+}
+
+
+static void test_glib(void) {
+ GMainContext *ctx = g_main_context_new();
+ GMainLoop *gloop = g_main_loop_new(ctx, FALSE);
+ evcon_allocator *alloc = evcon_glib_allocator();
+ evcon_loop *loop = evcon_loop_from_glib(ctx, alloc);
+ EchoClient *client;
+ EchoServer *srv;
+
+ srv = echo_server_new(loop);
+ g_debug("Listening on port %i\n", srv->port);
+
+ client = echo_client_new(srv, 5, test_glib_client_finished_cb, gloop);
+
+ g_main_loop_run(gloop);
+
+ echo_client_free(client);
+ echo_server_free(srv);
+
+ evcon_loop_unref(loop);
+
+ g_main_loop_unref(gloop);
+ g_main_context_unref(ctx);
+}
+
+
+
+int main(int argc, char** argv) {
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/evcon-echo/test-glib", test_glib);
+
+ return g_test_run();
+}