Browse Source

Initial commit

master
Stefan Bühler 7 years ago
commit
f5fc31c6b7
39 changed files with 2706 additions and 0 deletions
  1. +13
    -0
      .gitignore
  2. +22
    -0
      COPYING
  3. +12
    -0
      Makefile.am
  4. +65
    -0
      README.md
  5. +31
    -0
      autogen.sh
  6. +126
    -0
      configure.ac
  7. +11
    -0
      evcon-ev.pc.in
  8. +11
    -0
      evcon-event.pc.in
  9. +11
    -0
      evcon-glib.pc.in
  10. +11
    -0
      evcon.pc.in
  11. +2
    -0
      libevcon-ev0.symbols
  12. +2
    -0
      libevcon-event0.symbols
  13. +4
    -0
      libevcon-glib0.symbols
  14. +66
    -0
      libevcon0.symbols
  15. +1
    -0
      src/Makefile.am
  16. +16
    -0
      src/backend-ev/Makefile.am
  17. +165
    -0
      src/backend-ev/ev-backend.c
  18. +10
    -0
      src/backend-ev/evcon-ev.h
  19. +16
    -0
      src/backend-event/Makefile.am
  20. +10
    -0
      src/backend-event/evcon-event.h
  21. +157
    -0
      src/backend-event/event-backend.c
  22. +16
    -0
      src/backend-glib/Makefile.am
  23. +13
    -0
      src/backend-glib/evcon-glib.h
  24. +38
    -0
      src/backend-glib/glib-allocator.c
  25. +376
    -0
      src/backend-glib/glib-backend.c
  26. +16
    -0
      src/backend-qt/Makefile.am
  27. +14
    -0
      src/core/Makefile.am
  28. +35
    -0
      src/core/evcon-allocator.h
  29. +64
    -0
      src/core/evcon-backend.h
  30. +98
    -0
      src/core/evcon-config-private.h.in
  31. +9
    -0
      src/core/evcon-config.h.in
  32. +564
    -0
      src/core/evcon.c
  33. +114
    -0
      src/core/evcon.h
  34. +33
    -0
      src/tests/Makefile.am
  35. +365
    -0
      src/tests/evcon-echo.c
  36. +54
    -0
      src/tests/evcon-echo.h
  37. +45
    -0
      src/tests/evcon-test-ev.c
  38. +43
    -0
      src/tests/evcon-test-event.c
  39. +47
    -0
      src/tests/evcon-test-glib.c

+ 13
- 0
.gitignore View File

@@ -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

+ 22
- 0
COPYING View File

@@ -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.

+ 12
- 0
Makefile.am View File

@@ -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

+ 65
- 0
README.md View File

@@ -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, ...).

+ 31
- 0
autogen.sh View File

@@ -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."

+ 126
- 0
configure.ac View File

@@ -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

+ 11
- 0
evcon-ev.pc.in View File

@@ -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:

+ 11
- 0
evcon-event.pc.in View File

@@ -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:

+ 11
- 0
evcon-glib.pc.in View File

@@ -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:

+ 11
- 0
evcon.pc.in View File

@@ -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:

+ 2
- 0
libevcon-ev0.symbols View File

@@ -0,0 +1,2 @@
libevcon-ev.so.0 libevcon-ev0 #MINVER#
evcon_loop_from_ev@Base 0.1.0

+ 2
- 0
libevcon-event0.symbols View File

@@ -0,0 +1,2 @@
libevcon-event.so.0 libevcon-event0 #MINVER#
evcon_loop_from_event@Base 0.1.0

+ 4
- 0
libevcon-glib0.symbols View File

@@ -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

+ 66
- 0
libevcon0.symbols View File

@@ -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

+ 1
- 0
src/Makefile.am View File

@@ -0,0 +1 @@
SUBDIRS = core backend-glib backend-ev backend-event tests

+ 16
- 0
src/backend-ev/Makefile.am View File

@@ -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)

+ 165
- 0
src/backend-ev/ev-backend.c View File

@@ -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;
}

+ 10
- 0
src/backend-ev/evcon-ev.h View File

@@ -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

+ 16
- 0
src/backend-event/Makefile.am View File

@@ -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)

+ 10
- 0
src/backend-event/evcon-event.h View File

@@ -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

+ 157
- 0
src/backend-event/event-backend.c View File

@@ -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;
}

+ 16
- 0
src/backend-glib/Makefile.am View File

@@ -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)

+ 13
- 0
src/backend-glib/evcon-glib.h View File

@@ -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

+ 38
- 0
src/backend-glib/glib-allocator.c View File

@@ -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;
}

+ 376
- 0
src/backend-glib/glib-backend.c View File

@@ -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;
}

+ 16
- 0
src/backend-qt/Makefile.am View File

@@ -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)

+ 14
- 0
src/core/Makefile.am View File

@@ -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

+ 35
- 0
src/core/evcon-allocator.h View File

@@ -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

+ 64
- 0
src/core/evcon-backend.h View File

@@ -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

+ 98
- 0
src/core/evcon-config-private.h.in View File

@@ -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

+ 9
- 0
src/core/evcon-config.h.in View File

@@ -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

+ 564
- 0
src/core/evcon.c View File

@@ -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;
}

+ 114
- 0
src/core/evcon.h View File

@@ -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);