Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
304 lines (198 sloc) 10.3 KB


libeio - truly asynchronous POSIX I/O


#include <eio.h>


The newest version of this document is also available as an html-formatted web page you might find easier to navigate when reading it for the first time:

Note that this library is a by-product of the IO::AIO perl module, and many of the subtler points regarding requets lifetime and so on are only documented in its documentation at the moment:


This library provides fully asynchronous versions of most POSIX functions dealign with I/O. Unlike most asynchronous libraries, this not only includes read and write, but also open, stat, unlink and similar functions, as well as less rarely ones such as mknod, futime or readlink.

It also offers wrappers around sendfile (Solaris, Linux, HP-UX and FreeBSD, with emulation on other platforms) and readahead (Linux, with emulation elsewhere>).

The goal is to enable you to write fully non-blocking programs. For example, in a game server, you would not want to freeze for a few seconds just because the server is running a backup and you happen to call readdir.


Libeio represents time as a single floating point number, representing the (fractional) number of seconds since the (POSIX) epoch (somewhere near the beginning of 1970, details are complicated, don't ask). This type is called eio_tstamp, but it is guarenteed to be of type double (or better), so you can freely use double yourself.

Unlike the name component stamp might indicate, it is also used for time differences throughout libeio.


Calling fork () is fully supported by this module. It is implemented in these steps:

1. wait till all requests in "execute" state have been handled
   (basically requests that are already handed over to the kernel).
2. fork
3. in the parent, continue business as usual, done
4. in the child, destroy all ready and pending requests and free the
   memory used by the worker threads. This gives you a fully empty
   libeio queue.


Before you can call any eio functions you first have to initialise the library. The library integrates into any event loop, but can also be used without one, including in polling mode.

You have to provide the necessary glue yourself, however.

int eio_init (void (*want_poll)(void), void (*done_poll)(void))

This function initialises the library. On success it returns 0, on failure it returns -1 and sets errno appropriately.

It accepts two function pointers specifying callbacks as argument, both of which can be 0, in which case the callback isn't called.

want_poll callback

The want_poll callback is invoked whenever libeio wants attention (i.e. it wants to be polled by calling eio_poll). It is "edge-triggered", that is, it will only be called once when eio wants attention, until all pending requests have been handled.

This callback is called while locks are being held, so you must not call any libeio functions inside this callback. That includes eio_poll. What you should do is notify some other thread, or wake up your event loop, and then call eio_poll.

done_poll callback

This callback is invoked when libeio detects that all pending requests have been handled. It is "edge-triggered", that is, it will only be called once after want_poll. To put it differently, want_poll and done_poll are invoked in pairs: after want_poll you have to call eio_poll () until either eio_poll indicates that everything has been handled or done_poll has been called, which signals the same.

Note that eio_poll might return after done_poll and want_poll have been called again, so watch out for races in your code.

As with want_poll, this callback is called while lcoks are being held, so you must not call any libeio functions form within this callback.

int eio_poll ()

This function has to be called whenever there are pending requests that need finishing. You usually call this after want_poll has indicated that you should do so, but you can also call this function regularly to poll for new results.

If any request invocation returns a non-zero value, then eio_poll () immediately returns with that value as return value.

Otherwise, if all requests could be handled, it returns 0. If for some reason not all requests have been handled, i.e. some are still pending, it returns -1.

For libev, you would typically use an ev_async watcher: the want_poll callback would invoke ev_async_send to wake up the event loop. Inside the callback set for the watcher, one would call eio_poll () (followed by ev_async_send again if eio_poll indicates that not all requests have been handled yet). The race is taken care of because libev resets/rearms the async watcher before calling your callback, and therefore, before calling eio_poll. This might result in (some) spurious wake-ups, but is generally harmless.

For most other event loops, you would typically use a pipe - the event loop should be told to wait for read readyness on the read end. In want_poll you would write a single byte, in done_poll you would try to read that byte, and in the callback for the read end, you would call eio_poll. The race is avoided here because the event loop should invoke your callback again and again until the byte has been read (as the pipe read callback does not read it, only done_poll).


The functions in this section can sometimes be useful, but the default configuration will do in most case, so you should skip this section on first reading.

eio_set_max_poll_time (eio_tstamp nseconds)

This causes eio_poll () to return after it has detected that it was running for nsecond seconds or longer (this number can be fractional).

This can be used to limit the amount of time spent handling eio requests, for example, in interactive programs, you might want to limit this time to 0.01 seconds or so.

Note that:

a) libeio doesn't know how long your request callbacks take, so the time spent in eio_poll is up to one callback invocation longer then this interval.

b) this is implemented by calling gettimeofday after each request, which can be costly.

c) at least one request will be handled.

eio_set_max_poll_reqs (unsigned int nreqs)

When nreqs is non-zero, then eio_poll will not handle more than nreqs requests per invocation. This is a less costly way to limit the amount of work done by eio_poll then setting a time limit.

If you know your callbacks are generally fast, you could use this to encourage interactiveness in your programs by setting it to 10, 100 or even 1000.

eio_set_min_parallel (unsigned int nthreads)

Make sure libeio can handle at least this many requests in parallel. It might be able handle more.

eio_set_max_parallel (unsigned int nthreads)

Set the maximum number of threads that libeio will spawn.

eio_set_max_idle (unsigned int nthreads)

Libeio uses threads internally to handle most requests, and will start and stop threads on demand.

This call can be used to limit the number of idle threads (threads without work to do): libeio will keep some threads idle in preperation for more requests, but never longer than nthreads threads.

In addition to this, libeio will also stop threads when they are idle for a few seconds, regardless of this setting.

unsigned int eio_nthreads ()

Return the number of worker threads currently running.

unsigned int eio_nreqs ()

Return the number of requests currently handled by libeio. This is the total number of requests that have been submitted to libeio, but not yet destroyed.

unsigned int eio_nready ()

Returns the number of ready requests, i.e. requests that have been submitted but have not yet entered the execution phase.

unsigned int eio_npending ()

Returns the number of pending requests, i.e. requests that have been executed and have results, but have not been finished yet by a call to eio_poll).








Libeio can be embedded directly into programs. This functionality is not documented and not (yet) officially supported.

Note that, when including libeio.m4, you are responsible for defining the compilation environment (_LARGEFILE_SOURCE, _GNU_SOURCE etc.).

If you need to know how, check the IO::AIO perl module, which does exactly that.


These symbols, if used, must be defined when compiling eio.c.


This symbol governs the stack size for each eio thread. Libeio itself was written to use very little stackspace, but when using EIO_CUSTOM requests, you might want to increase this.

If this symbol is undefined (the default) then libeio will use its default stack size (sizeof (long) * 4096 currently). If it is defined, but 0, then the default operating system stack size will be used. In all other cases, the value must be an expression that evaluates to the desired stack size.


In addition to a working ISO-C implementation, libeio relies on a few additional extensions:

POSIX threads

To be portable, this module uses threads, specifically, the POSIX threads library must be available (and working, which partially excludes many xBSD systems, where fork () is buggy).

POSIX-compatible filesystem API

This is actually a harder portability requirement: The libeio API is quite demanding regarding POSIX API calls (symlinks, user/group management etc.).

double must hold a time value in seconds with enough accuracy

The type double is used to represent timestamps. It is required to have at least 51 bits of mantissa (and 9 bits of exponent), which is good enough for at least into the year 4000. This requirement is fulfilled by implementations implementing IEEE 754 (basically all existing ones).

If you know of other additional requirements drop me a note.


Marc Lehmann <>.


Hey! The above document had some coding errors, which are explained below:

Around line 229:

=back without =over