s6
Software
skarnet.org

# libftrig

`libftrig` is a portable Unix C programming interface allowing a process (the subscriber or listener) to be instantly notified when another process (the notifier or writer) signals an event.

## What is notification ?

### Notification vs. polling

Process A is notified of an event E when it gets a instant notice that event E has happened; the notice may disrupt A's execution flow. Notification is the opposite of polling, where A has to periodically (every T milliseconds) check whether E happened and has no other way to be informed of it.

Polling is generally considered bad practice - and is inferior to notification in practically every case - for three reasons:

- Reaction time. When event E happens, process A does not know it instantly. It will only learn of E, and be able to react to it, when it explicitly checks for E; and if E happened right after A performed the check, this can take as long as T milliseconds (the polling period). Polling processes have reaction delays due to the polling periods.
- Resource consumption. Even if no event ever happens, process A will still wake up needlessly every T milliseconds. This might not seem like a problem, but it is a serious one in energy-critical environments. Polling processes use more CPU time than is necessary and are not energy-friendly.
- Conflict between the two above reasons. The longer the polling period, the more energy-friendly the process, but the longer the reaction time. The shorter the polling period, the shorter the reaction time, but the more resource-consuming the process. A delicate balance has to be found, and acceptable behaviour is different in every case, so there's no general rule of optimization.

Notification, on the other hand, is generally optimal: reaction time is zero, and resource consumption is minimal - a process can sleep as soon as it's not handling an event, and only wake up when needed.

Of course, the problem of notification is that it's often more difficult to implement. Notification frameworks are generally more complex, involving lots of asynchronism; polling is widely used because it's easy.

### Notifications and Unix

Unix provides several frameworks so that a process B (or the kernel) can notify process A.

- Signals. The simplest Unix notification mechanism. Sending events amounts to a kill() call, and receiving events amounts to installing a signal handler (preferably using a self-pipe if mixing signals with an event loop). Unfortunately, Unix signals, even the more recent and powerful real-time POSIX signals, have important limitations when it's about generic notification:
  - non-root processes can only send signals to a very restricted and implementation-dependent set of processes (roughly, processes with the same UID). This is a problem when designing secure programs that make use of the Unix privilege separation.
  - you need to know the PID of a process to send it signals. This is generally impractical; process management systems that do not use supervisor processes have to do exactly that, and they resort to unreliable, ugly hacks (.pid files) to track down process PIDs.
- BSD-style IPCs, i.e. file descriptors to perform select()/poll() system calls on, in an asynchronous event loop. This mechanism is very widely used, and rightly so, because it's extremely generic and works in every ordinary situation; you have to be doing very specific stuff to reach its limits. If process A is reading on fd f, it is notified everytime another process makes f readable - for instance by writing a byte to the other end if f is the reading end of a pipe. And indeed, this is how libftrig works internally; but libftrig is needed because direct use of BSD-style IPCs also has limitations.
  - Anonymous pipes are the simplest and most common BSD-style IPC. If there is a pipe from process B to process A, then B can notify A by writing to the pipe. The limitation is that A and B must have a common ancestor that created the pipe; two unrelated processes cannot communicate this way.
  - Sockets are a good many-to-one notification system: once a server is up, it can be notified by any client, and notify all its clients. The limitation of sockets is that the server must be up before the client, which prevents us from using them in a general notification scheme.
- System V IPCs, i.e. message queues and semaphores. The interfaces to those IPCs are quite specific and can't mix with select/poll loops, that's why nobody in their right mind uses them.

### What we want

We need a general framework to:

- Allow an event-generating process to broadcast notifications to every process that asked for one, without having to know their PIDs
- Allow a process to subscribe to a "notification channel" and be instantly, asynchronously notified when an event occurs on this channel.

This requires a many-to-many approach that Unix does not provide natively, and that is what libftrig does.

## That's what a bus is for. D-Bus already does all this.

Yes, a bus is a good many-to-many notification mechanism indeed. However, a Unix bus can only be implemented via a daemon - you need a long-running process, i.e. a service, to implement a bus. And s6 is a supervision suite, i.e. a set of programs designed to manage services; we would like to be able to use notifications in the supervision suite, to be able to wait for a service to be up or down... without relying on a particular service to be up. libftrig provides a notification mechanism that does not need a bus service to be up, that's its main advantage over a bus.

If you are not concerned with supervision and can depend on a bus service, though, then yes, by all means, use a bus for your notification needs. There is a skabus project in the making, which aims to be simpler, smaller and more maintainable than D-Bus.

## How to use libftrig

`libftrig` is really a part of libs6: all the functions are implemented in the `libs6.a` archive, or the `libs6.so` dynamic shared object. However, the interfaces are different for notifiers and listeners:

- Notifiers use the ftrigw interface.
- Listeners use the ftrigr interface.