skalibs: the selfpipe library interface
Keywords: skalibs stddjb libstddjb selfpipe self-pipe library interface

libstddjb
libskarnet
skalibs
Software
skarnet.org

# The `selfpipe` library interface

The selfpipe functions are declared in the `skalibs/selfpipe.h` header and implemented in the `libskarnet.a` or `libskarnet.so` library.

## What does it do ?

Signal handlers suck.

They do. I don't care how experienced you are with C/Unix programming, they do. You can be Ken Thompson, if you use signal handlers as a regular part of your C programming model, you are going to screw up, and write buggy code.

Unix is tricky enough with interruptions. Even when you have a single thread, signals can make the execution flow very non-intuitive. They mess up the logic of linear and structured code, they introduce non-determinism; you always have to think "and what if I get interrupted here and the flow goes into a handler...". This is annoying.

Moreover, signal handler code is very limited in what it can do. It can't use any non-reentrant function! If you call a non-reentrant function, and by chance you were precisely in that non-reentrant function code when you got interrupted by a signal... you lose. That means, no malloc(). No bufferized IO. No globals. The list goes on and on.
If you're going to catch signals, you'll want to handle them outside the signal handler. You actually want to spend the least possible time inside a signal handler - just enough to notify your main execution flow that there's a signal to take care of.

And, of course, signal handlers don't mix with event loops, which is a classic source of headaches for programmers and led to the birth of abominations such as pselect. So much for the "everything is a file" concept that Unix was built on.

A signal should be an event like any other. There should be a unified interface - receiving a signal should make some fd readable or something.

And that's exactly what the self-pipe trick, invented by DJB, does.

As long as you're in some kind of event loop, the self-pipe trick allows you to forget about signal handlers... forever. It works this way:

1. Create a pipe `p`. Make both ends close-on-exec and nonblocking.
2. Write a tiny signal handler ("top half") for all the signals you want to catch. This signal handler should just write one byte into `p[1]`, and do nothing more; ideally, the written byte identifies the signal.
3. In your event loop, add `p[0]` to the list of fds you're watching for readability.

When you get a signal, a byte will be written to the self-pipe, and your execution flow will resume. When you next go through the event loop, `p[0]` will be readable; you'll then be able to read a byte from it, identify the signal, and handle it - in your unrestricted main environment (the "bottom half" of the handler).

The selfpipe library does it all for you - you don't even have to write the top half yourself. You can forget their existence and recover some peace of mind.

Note that in an asynchronous event loop, you need to protect your system calls against EINTR by using safe wrappers.

## How do I use it ?

### Starting

    int fd = selfpipe_init() ;

`selfpipe_init()` sets up a selfpipe. You must use that function first.
If `fd` is -1, then an error occurred. Else `fd` is a non-blocking descriptor that can be used in your event loop. It will be selected for readability when you've caught a signal.

### Trapping signals

    int r = selfpipe_trap(SIGTERM) ;

`selfpipe_trap()` catches a signal and sends it to the selfpipe. Uncaught signals won't trigger the selfpipe. `r` is 1 if the operation succeeded, and 0 if it failed. If it succeeded, you can forget about the trapped signal entirely.
In our example, if `r` is 1, then a SIGTERM will instantly trigger readability on `fd`.

    int r ;
    sigset_t set ;
    sigemptyset(&set) ;
    sigaddset(&set, SIGTERM) ;
    sigaddset(&set, SIGHUP) ;
    r = selfpipe_trapset(&set) ;

`selfpipe_trap()` handles signals one by one. Alternatively (and often preferrably), you can use `selfpipe_trapset()` to directly handle signal sets. When you call `selfpipe_trapset()`, signals that are present in `set` will be caught by the selfpipe, and signals that are absent from `set` will be uncaught. `r` is 1 if the operation succeeded and 0 if it failed.

### Handling events

    int c = selfpipe_read() ;

Call `selfpipe_read()` when your `fd` is readable. That's where you write your real signal handler: in the body of your event loop, in a "normal" context.
`c` is -1 if an error occurred - in which case chances are it's a serious one and your system has become very unstable. `c` is 0 if there are no more pending signals. If `c` is positive, it is the number of the signal that was caught.

### Finishing

    selfpipe_finish() ;

Call `selfpipe_finish()` when you're done using the selfpipe. Signal handlers will be restored to SIG_DFL, i.e. signals will not be trapped anymore.

## Any limitations ?

Some, as always.

- The selfpipe library uses a global pipe; so, it's theoretically not safe for multithreading. However, as long as you dedicate one thread to signal handling and block signals in all the other threads (see pthread_sigmask()) then you should be able to use the selfpipe in the thread that handles signals without trouble. Since reading the selfpipe involves waiting for a file descriptor to become readable, it is recommended to do this in a thread that will already have a regular input/output loop (via poll() or iopause()) so you can just add the selfpipe to the list of fds you're reading on.
- In rare cases, the self-pipe can theoretically be filled, if some application sends more than PIPE_BUF signals before you have time to `selfpipe_read()`. On most Unix systems, PIPE_BUF is 4096, so it's a very acceptable margin. Unless your code is waiting where it should not be, only malicious applications will fill the self-pipe - and malicious applications could just send you a SIGKILL and be done with you, so this is not a concern. Protect yourself from malicious applications with clever use of uids.

## Hey, Linux has signalfd() for this !

Yes, the Linux team loves to gratuitously add new system calls to do things that could already be done before without much effort. This adds API complexity, which is not a sign of good engineering.

However, now that `signalfd()` exists, it is indeed marginally more efficient than a pipe, and it saves one fd: so the selfpipe library is implemented via `signalfd()` when this call is available.