NAME

evtid - The API for events driven network programming


SYNOPSIS

        #include "evtid.h"
        evti_t tmr;
        evtid_init();
        evti_init(&tmr);
        tmr.action = my_timer_action;
        // call my_timer_action after 2 seconds
        evti_start_s(&tmr, 2);
        // dispatch the events
        evtid_dispatcher();
        evtid_fini();


DESCRIPTION

This small (and fairly simple) library is an attempt to provide simplified interface to select, poll, epoll found on *nix systems (and probably on some others :)

The library allows to handle timed events and read/write readinnes of file descriptors. It does not use any trees to store data, so it is very light-weight. Generally it provides the same functionality as libevent library. The latter is more fundamental but probably slower when it comes to timer handling. See more on this in GENERAL NOTES later.

THE API DESCRIPTION

GENERIC FUNCTIONS

TIMER API

The timers are structures of type evti_t. The definition of this structure is

        typedef void (*evti_handler_t)(evti_t *);
        typedef struct evti_t evti_t;
        struct evti_t {
            evti_handler_t action;
            evit_internal_t _data;
        };

Here are the functions that work with this structure

Marking periods of time

In certain cituations it might be desired to check how much time has elapsed since certain moment of time. Here's small set of functions that can be used for this. They work with the structure evti_mark_t. The content of this structure is not important to the user.

FILE DESCRIPTOR API

The file descriptor events are distributed using the following structure

        typedef void (*evd_handler_t)(evd_t*);
        typedef struct evd_t evd_t;
        struct evd_t{
             int d;
             evd_handler raction;
             evd_handler waction;
             evd_internal_t _data;
        };

Here, field ``d'' is the descriptor number, fields ``raction'' and ``waction'' define handlers that shall be called when descriptor is considered to be read-ready and write-ready.

GENERAL NOTES

There are no void pointers in this implementation. Both evd_t and evti_t structures have internal fields that allow them to be linked into library's structures. Of course, usually the handlers need additional information for processing events. This information is easy to access if evd_t or evti_t is part of some bigger structure. For example:

        struct my_test{
            evd_t evd;
            int my_id;
        };

Here pointer to evd is exactly the same as pointer to struct my_test. So simple cast is needed to access my_id field. In more complex cases, the ``pointed'' structure might be in the middle of ``containig'' structure. Then, to obtain pointer to the container, offset of that field has to be substracted from pointer to the field. There's macro ``offsetof'' defined in stddef.h (on linux), or alternatively macro ``predok'' defined in this library, that may help with this ariphmetic. So one can write

        struct my_test *ptr = predok(evd_ptr, struct my_test, evd);

This will calculate pointer to struct my_test from the pointer to field evd of that structure.

The library converts notifications about errors into read/write readinnes. Anyway, the errors are usually obtained via reading/writing functions.

This library does not attempt to handle signals. And it shouldn't really. The following piece of code can convert asynchronous signals into synchronous events. Well, provide error checking etc.

        static int sig_pipe;
        static evd_t sig_evd;
        static void
        signal_arrived(evd_t * evd)
        {
            // actions for handling the signals;
        }
        static void
        piper(int signo)
        {
            int err = errno;
            // write to sig_pipe your message
            write(sig_pipe, &err, sizeof(err));
            errno = err;
        }
        static void
        setup_sighandler(void)
        {
            struct sigaction act;
            int pp[2];
            pipe(pp);
            sig_pipe = pp[1];
            evd_init(&sig_evd);
            sig_evd.d = pp[0];
            sig_evd.raction = signal_arrived;
            sig_evd.waction = NULL;
            evd_watch(&sig_evd);
            evd_raction_active(&sig_evd);
            evd_input_exhausted(&sig_evd);
            act.sa_handler = piper;
            act.sa_flags = 0;
            sigemptyset(&act.sa_mask);
            sigaction(SIGHUP, &act, NULL);
        }

Timers implementation

Newer linux distributions provide function clock_gettime. This function reports how much time has elapsed since certain moment of time. If this function is available, then it shall be used for tracing elapsed time. Otherwise, define flag EVTI_WITH_ITIMER and the itimer functions shall be used for it. Which means that ITIMER_REAL is used up and is not available for other purposes. (Why would you need it anyway, when you have timers). Subsequently, the SIGALRM is also handled by the library. In return, the library is independent from the system time, which can be changed at any moment.

Currently, the smallest time resolution is 1 ms. That's what poll allows. Anyway, smaller times can't be handled correctly on vanilla linux and most other OS. Even 1 ms is somewhat questionable.

The implementation does not use any trees to store timers. Since in most of the applications (I had chance to write) the timers are needed as a time-out for some event, and these time-outs rarely expire, I use the approach they have in Linux kernel (and probably other places). So, there are slots for first 64 ms, and there are 64 more slots for groups of length 64 ms each. All other times are placed in an unsorted linked list. So once in 64 ms I may run thru the small group that contains timers expiring in next 64 ms, and ones in 4.096 seconds I run thru the longer list that contains all other timers and rearrange them into approprate place within groups.

USAGE

To use the library, simply compile evtid.c and link it with your code. When EVD_WITH_EPOLL is defined, then the epoll shall be used for checking readinnes. Defining EVD_WITH_SELECT results in using select, otherwise poll is used.


COMPATIBILITY

I've run it on NetBSD and Linux. No other platforms are available to me.


AUTHOR

Andrei A. Voropaev <voropaev.andrey@gmail.com>