| --- |
| title: facil.io - lib evio, kqueue/epoll abstraction in C. |
| toc: true |
| layout: api |
| --- |
| # The Evented IO Library - KQueue/EPoll abstraction |
| |
| The `evio.h` library, is a KQueue/EPoll abstraction for edge triggered events and is part of [`facil.io`'s](./facil.md) core. |
| |
| It should be noted that exactly like `epoll` and `kqueue`, `evio.h` might produce unexpected results if forked after initialized, since this will cause the `epoll`/`kqueue` data to be shared across processes, even though these processes will not have access to new file descriptors (i.e. `fd` 90 on one process might reference file "A" while on a different process the same `fd` (90) might reference file "B"). |
| |
| This documentation isn't relevant for `facil.io` users. `facil.io` implements `evio.h` callbacks and `evio.h` cannot be used without removing `facil.h` and `facil.c` from the project. |
| |
| This file is here as quick reference to anyone interested in maintaining `facil.io` or learning about how it's insides work. |
| |
| ## Event Callbacks |
| |
| Event callbacks are defined during the linking stage and are hardwired once compilation is complete. |
| |
| `void evio_on_data(void *)` - called when the file descriptor has incoming data. This is **one-shot** triggered, meaning it will **not** be called again unless the `evio_add` (or `evio_set_timer`) are called. |
| |
| `void evio_on_ready(void *)` - called when the file descriptor is ready to send data (outgoing). |
| |
| `void evio_on_error(void *)` - called when a file descriptor raises an error while being polled. |
| |
| `void evio_on_close(void *)` - called when a file descriptor was closed REMOTELY. `evio_on_close` will NOT get called when a connection is closed locally, unless using `sock.h`'s callback, the `sock_on_close` function. |
| |
| **Notice**: Both EPoll and KQueue will **not** raise an event for an `fd` that was closed using the native `close` function, so unless using `sock.h` or calling `evio_on_close`, the `evio_on_close` callback will only be called for remote events. |
| |
| **Notice**: The `on_open` event is missing by design, as it is expected that any initialization required for the `on_open` event will be performed before attaching the file descriptor (socket/timer/pipe) to `evio` using [`evio_add`](). |
| |
| ## The `evio` API |
| |
| |
| ### `evio_create` |
| |
| ```c |
| intptr_t evio_create(void) |
| ``` |
| |
| Creates the `epoll` or `kqueue` object. |
| |
| It's impossible to add or remove file descriptors from the polling system before |
| calling this method. |
| |
| Returns -1 on error, otherwise returns a unique value representing the `epoll` |
| or `kqueue` object. The returned value can be safely ignored. |
| |
| **NOTE**: Once an `epoll` / `kqueue` object was opened, `fork` should be avoided, |
| since ALL the events will be shared among the forked processes (while not ALL |
| the file descriptors are expected to be shared). |
| |
| ### `evio_review` |
| |
| ```c |
| int evio_review(const int timeout_millisec) |
| ``` |
| |
| Reviews any pending events (up to `EVIO_MAX_EVENTS`) and calls any callbacks. |
| |
| Waits up to `timeout_millisec` for events to occur. |
| |
| Returns -1 on error, otherwise returns the number of events handled. |
| |
| ### `evio_close` |
| |
| ```c |
| void evio_close(void); |
| ``` |
| |
| Closes the `epoll` / `kqueue` object, releasing it's resources (important if |
| forking!). |
| |
| ### `evio_isactive` |
| |
| ```c |
| int evio_isactive(void) |
| ``` |
| |
| Returns true if the evio is available for adding or removing file descriptors. |
| |
| |
| ### `evio_add` |
| |
| ```c |
| int evio_add(int fd, void *callback_arg) |
| ``` |
| |
| Adds a file descriptor to the polling object (ONE SHOT). |
| |
| Returns -1 on error, otherwise return value is system dependent and can be |
| safely ignored. |
| |
| ### `evio_open_timer` |
| |
| ```c |
| intptr_t evio_open_timer(void) |
| ``` |
| |
| Creates a timer file descriptor, system dependent. |
| |
| Returns -1 on error, or a valid fd on success. |
| |
| NOTE: Systems have a limit on the number of timers that can be opened. |
| |
| ### `evio_set_timer` |
| |
| ```c |
| intptr_t evio_set_timer(int fd, void *callback_arg, unsigned long milliseconds) |
| ``` |
| |
| Adds a timer file descriptor, so that callbacks will be called for it's events. |
| |
| Returns -1 on error, otherwise return value is system dependent. |
| |
| ## Known Issues |
| |
| * On Linux, using `epoll`, the `EPOLLONESHOT` flag doesn't remove the `fd` from `epoll` descriptor, requiring `EPOLL_CTL_MOD` instead of `EPOLL_CTL_ADD`. |
| |
| To support a stateless API, where the user doesn't need to remember if a specific `fd` was previously passed to the `evio_add` function, the `epoll` implementation performs two system calls for new connections instead of a single system call (`EPOLL_CTL_ADD` is performed only if `EPOLL_CTL_MOD` fails). |