blob: 0357d413f72aa22e02a9860eede6da12d2d44f0d [file] [log] [blame] [view] [raw]
---
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).