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.