event

NAME
SYNOPSYS
DESCRIPTION
RETURN VALUES
SEE ALSO
BUGS
AUTHOR

NAME

event − timer and I/O event manager

SYNOPSYS

#include <event.h>

struct ev_ct;
struct ev_tm;

struct ev_ct *ev_ct_new(int maxfdhint, int typeflags)
int ev_init(int maxfdhint, int typeflags)
void ev_ct_free(struct ev_ct *ct)
void ev_free(void)

int ev_method(const struct ev_ct *ct)
const char *ev_method_name(const struct ev_ct *ct)
int ev_fdlimit(void)

typedef void ev_io_cbck_f(void *data, int revents,
                          int fd, struct ev_ct *ct)
int ev_io_add(struct ev_ct *ct, int fd, int events,
              ev_io_cbck_f *cbck, void *data)
int ev_io_mod(struct ev_ct *ct, int fd, int events,
              ev_io_cbck_f *cbck, void *data)
int ev_io_del(struct ev_ct *ct, int fd)
int ev_io_count(const struct ev_ct *ct)

typedef void ev_tm_cbck_f(void *data,
    struct ev_tm *tmr, struct ev_ct *ct)
extern ev_time_t ev_now
extern time_t ev_time
ev_time_t ev_gettime(void)
struct ev_tm *ev_tm_add(struct ev_ct *ct, int mstimeout,
   struct ev_tm *tmr, ev_tm_cbck_f *cbck, void *data)
struct ev_tm *ev_ts_add(struct ev_ct *ct, int stimeout,
   struct ev_tm *tmr, ev_tm_cbck_f *cbck, void *data)
int ev_tm_del(struct ev_ct *ct, struct ev_tm *tmr)
int ev_tm_count(const struct ev_ct *ct)
ev_time_t ev_tm_first(const struct ev_ct *ct)
int ev_tm_timeout(const struct ev_ct *ct)

int ev_wait(struct ev_ct *ct, int mstimeout)

DESCRIPTION

The event module implements a simple threading core that allows a process to wait for multiple I/O and/or timer events. Multiple I/O streams and timers can be monitored simultaneously. Events are delivered via callback routines provided by the application. When requesting an event, application provides private context that is passed back to the callback routine when the routine is executed, together with some more information about the event.

There are two main types of events recognized by the module, and two types of callback routines are defined: I/O events, i.e. when a filedescriptor becomes readable or writable, and timer events, which gets executed once at a given time (specified as an offset from current time). Timers have millisecond resolution.

The module uses one of several different underlying mechanisms for event monitoring, including traditional select(2) and poll(2), and more advanced methods available for a given operating system, such as epoll(4), kqueue(2) and devpoll (poll(7d)).

There are several sets of routines provided by the module, for the following tasks: initialisation, adding/modifying/removing I/O watchers (keyed by a filedescriptor), adding/removing timers, and performing actual event waiting and dispatching. The following subsections describes each group of routines in turn.

Initialisation

Every routine in this module requires an event context of type struct ev_ct (opaquie to the application), that holds internal state of the module, to be passed as a first argument et. This argument may be NULL pointer, in which case a default event context is used (which should be initialized too). This way, it is possible to use several "contexts", or instances, of the module (for example when an application itself is multi-threaded, with it’s own event context in every thread). To initialize and free event context, the following routines are provided:

struct ev_ct *ev_ct_new(int maxfdhint, int typeflags)
int ev_init(int maxfdhint, int typeflags)

creates and initializes new event context − either specific, or global default that will be used when no context is provided (when ct argument for other routines described below is NULL). One of this routines (or both, to initialize either as many specific or single global context) should be called before any other routine in this module.

ev_ct_new() return either pointer to newly allocated and initialized event context, or NULL in case of error; ev_init() return 0 on success or negative value on error. In case of error, global variable errno will hold specific error information.

Parameter maxfdhint specifies maximum filedescriptor that application expects to monitor (only as a hint, to allocate enouth resources in one go instead of reallocating new memory on demand). If maxfdhint is <= 0, the module will determine maximum number of files a process may open, using ev_fdlimit() routine (see below).

The typeflags parameter (a bitmask) specifies which underlying method the module should use for event monitoring. Module will choose the "best" method that matches typeflags from ones which are available on a given system. typeflags may be 0, in which case any method may be used. Currently, the following methods are recognized (some are platform-specific):

EV_EPOLL

epoll, available on Linux 2.6 and above, or with patch on 2.4.

EV_KQUEUE

kqueue, available on FreeBSD, OpenBSD and some others.

EV_DEVPOLL

/dev/poll interface with ioctl(), available on Solaris.

EV_POLL

traditional poll(2) interface, problematic to use when number of filedescriptors being monitored is large.

EV_SELECT

traditional select(2) interface, most portable, also problematic when number of filedescriptors is large, and usually have upper limit of 1024 filedescriptors (which may be avoided on some systems).

EV_ADVANCED

a shortcut for all the more "advanced" methods, the module will choose one available on a given platform if any.

Another way to specify which method to use is to set $EV_METHOD environment variable to one of the following values: epoll, kqueue, devpoll, poll, select.

The following error conditions are possible:

ENOTSUP or ENOSYS

method(s) requested by typeflags (or by $EV_METHOD variable) isn’t supported

ENOMEM

there is no memory available to initialize structure

void ev_ct_free(struct ev_ct *ct)
void ev_free(void)

deallocates resources assotiated with the given specific event context ct or global default context. Calling ev_ct_free() with NULL ct argument is the same as calling ev_free(). Note that these routines does not close opened files or frees timer structures which may be assotiated with the event context, this is the responsibility of an application. Usually, freeing an event context that still have some timers or I/O watchers assotiated with it is an error.

int ev_method(const struct ev_ct *ct)
const char *ev_method_name(const struct ev_ct *ct)

return the code (described near ev_init() above) or name of underlying operating system mechanism used for monitoring.

int ev_fdlimit(void)

return number of filedescriptors a process may open, according to getrlimit(2) system call.

Monitoring I/O availability

The module may monitor a set of filedescriptors and call application−supplied callback routine (of type ev_io_cbck_f) when a filedescriptor becomes readable, writable or have an exceptional condition. An application registers a filedescriptor to be monitored, together with a set of conditions of interest, a pointer to callback routine and a pointer to application-specific data. When any of conditions becomes available, module will execute callback routine, passing it the data pointer, a bitmask indicating which conditions become true, and a filedescriptor in question, together with a pointer to assotiated event context. Available conditions are:

EV_IN

filedescriptor is readable (there’s some input data to be read), or for end of file.

EV_OUT

filedescriptor is writable, usually for sockets when underlying network stack sent buffered data to a peer and more buffer space become available.

EV_PRI

there’s urgent data to be read.

The following types and routines are defined:

typedef void ev_io_cbck_f(void *data, int revents,
                          int fd, struct ev_ct *ct)

The type of application-supplied callback routine which will be called by the module when any of the conditions of interest becomes true. ct is the event context assotiated with the event, either specific or default but is never NULL. fd is the file descriptor in question. revents is a bitmask indicating which conditions (EV_IN, EV_OUT or EV_PRI) are true for the filedescriptor. data is the application-specific data that was passed to ev_io_add() or ev_io_mod() (below) when the fd where registered.

It is ok to add/remove events from within the callback routine.

Note that C language calling rules allows one to use a routine that accepts only a subset of arguments. Namely, a routine that expects only one argument, a pointer to application data (e.g. a structure describing server connection), will act as a callback just fine.

int ev_io_add(struct ev_ct *ct, int fd, int events,
              ev_io_cbck_f *cbck, void *data)

Registers the new filedescriptor fd to be monitored for conditions specified by events parameter, to call a callback routine cbck with data data when any of the conditions becomes true. Routine return 0 if registration was successeful, or negative value on error (and sets errno variable appropriately). It is an error to register a filedescriptor twice. The following error conditions are possible:

EEXIST

fd is already registered with a given event context (for ev_io_add() only)

ENOENT

fd is not registered with a given event context (for ev_io_mod() only, see below)

EINVAL

fd is negative

EMFILE

fd is too large for underlying monitoring mechanism to handle (e.g. select(2) usually unable to work when fd >= 1024)

ENOMEM

there is no memory available to initialize internal structure

EFAULT

cbck is NULL

int ev_io_mod(struct ev_ct *ct, int fd, int events,
              ev_io_cbck_f *cbck, void *data)

Modifies monitoring parameters for already registered filedescriptor fd. Parameters and return value are the same of ev_io_add().

int ev_io_del(struct ev_ct *ct, int fd)

Deregisters filedescriptor fd and return 0 on success or negative value (and sets errno) on failure. It is ok to deregister already closed filedescriptor fd. The following error conditions are possible:

ENOENT

fd is not registered with this event context.

int ev_io_count(const struct ev_ct *ct)

return number of filedescriptors currently registered with the given event context ct. The number may be used to watch when there’s no filedescriptors to be monitored, as a condition to exit event loop for example. There’s no error return.

Timers

In addition to I/O events, the module also implements concept of a timer, which is once-triggering event based on time. Timer events are delivered by callbacks in a way similar to I/O events. Unlike I/O events, each timer is assotiated with a structure which is owned by application and have to be allocated and freed appropriately (or it may be a part of some larger application structure). When timer event is triggered (i.e. when the module calls the application-supplied callback routine), the timer is already removed from the list of active timers, and pointer to timer structure is passed to the routine. An application may free the storage if it was dynamically allocated, or reuse the timer structure (to implement repeating timers).

Module caches current time to reduce system call overhead, updating it during initialisation and at each call to ev_wait() dispatching routine (below). In most cases this is sufficient, but an application may update the cached time by calling ev_gettime() routine (below). Cached current time is stored in ev_time (of type time_t, with secound resolution), and in ev_now (of type long long) global variables.

The following types, routines and variables are provided:

typedef void ev_tm_cbck_f(void *data,
    struct ev_tm *tmr, struct ev_ct *ct)

The type of timer callback routine. When the module calls the timer callback routine, it passes application−registered data pointer data, pointer to the timer structure tmr and assotiated event context ct to it. When the callback routine is executed, the timer in question tmr was already removed from set of active timers and was disassotiated from the event context, and may be reused or freed by an application as appropriate.

It is ok to add/remove events from within the callback routine, and tmr structure may be reused to (re−)add a timer as appropriate.

The same note as given for ev_io_cbck_f callback applies here as well: actual callback may expect and handle less parameters than the module passes to it (e.g. usually, only data pointer is sufficient for an application).

extern ev_time_t ev_now
extern time_t ev_time
ev_time_t ev_gettime(void)

cached current time in secounds (ev_time) or millisecounds (ev_now), and routine that updates the cache and return the same value as it has just stored in ev_now. Type ev_time_t is a 64bit integer (long long). There is no error return.

struct ev_tm *ev_tm_add(struct ev_ct *ct, int mstimeout,
   struct ev_tm *tmr, ev_tm_cbck_f *cbck, void *data)

Registers new timer event to be triggered after mstimeout millisecounds from now (since ev_now). When the timer will be triggered, the module will call the callback cbck with the value data. Argument tmr may be either NULL, in which case the routine will allocate new timer structure dynamically and return it upon successeful completion, or timer structure already allocated by application, in which case it shold be initialized to zero, and must not already be registered. In either case the application is responsible for freeing memory hold by tmr when it will be disassotiated from the event context (either when the module will execute callback routine or after ev_tm_del). Routine return pointer to the timer structure, or NULL in case of error (and sets errno appropriately).

Timer structure is opaque for an application, and should be zero-initialized on allocation. The only two fields assessible by the applications are:

  ev_tm_cbck_f *evtm_cbck
  void *evtm_data

which holds the pointer to the callback routine and the application-supplied data. Both fields may be modified by application while the timer is assotiated with an event context, with the exception that evtm_cbck can not be NULL.

Possible errno values after call to ev_tm_add() are:

EFAULT

cbck parameter is NULL

EINVAL

mstimeout value is negative

ENOMEM

tmr is NULL and there is no memory to allocate new structure.

struct ev_tm *ev_ts_add(struct ev_ct *ct, int stimeout,
   struct ev_tm *tmr, ev_tm_cbck_f *cbck, void *data)

similar to ev_tm_add(), with the difference that this one expects timeout in secounds instead of millisecounds, and tries to fire all timers sheduled for the same secound at once (even if they where registered at different millisecounds).

int ev_tm_del(struct ev_ct *ct, struct ev_tm *tmr)

Removes the given timer tmr, which should be registered with the event context ct by ev_tm_add() routine above. As usual, the application owns the tmr structure after the call to this routine. Routine return amount of millisecounds left to the time when the timer should be triggered (which may be 0), or negative value on error. In an attempt to remove a timer which isn’t registered (or has been triggered already), routine will indicate error and set errno to ENOENT.

int ev_tm_count(const struct ev_ct *ct)

return number of timers registered with a given event context ct.

ev_time_t ev_tm_first(const struct ev_ct *ct)
int ev_tm_timeout(const struct ev_ct *ct)

return a time when first timer will be triggered (or 0 if no timers are registered), and amount of millisecounds left to that time (or -1).

Event Loop

The main event loop handling routines are as follows:

int ev_wait(struct ev_ct *ct, int mstimeout)

one−shot wait−and−dispatch routine. It waits up to mstimeout millisecounds (specify negative value for to wait forever) for registered events to happen and executes all necessary callback routines, and when returns. Return value indicates how many I/O events where handled (i.e. how many filedescriptors where ready), which may be 0 in case of timeout or a timer expired, or -1 in case of error. If error occured (typical case is interrupt, in which case errno will be set to EINTR), ev_wait() still executes any pending timers and updates current time cache. For real event loop, an application should call this routine repeatedly until some condition (e.g. number of filedescriptors registered is non-zero) is true.

RETURN VALUES

Most routines in the module return zero or positive integer value in case of error

SEE ALSO

select(2), poll(2), epoll(4), kqueue(2), poll(7d), gettimeofday(2), time(2).

BUGS

The module is using non-standard time representation (ev_time_t type which is currently defined as long long). This may be not portable. But using any standard types (struct timeval, struct timespec and the like) complicates code significantly.

AUTHOR

This software was written by Michael Tokarev, <mjt@corpit.ru>, with help of ideas from work by Wietse Venema.