From 3534b428629be185e096be99e3bd5fdfe32d5544 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Thu, 18 Sep 2014 18:55:44 +0000 Subject: initial commit with rc for skalibs-2.0.0.0 --- doc/libstddjb/selfpipe.html | 242 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 doc/libstddjb/selfpipe.html (limited to 'doc/libstddjb/selfpipe.html') diff --git a/doc/libstddjb/selfpipe.html b/doc/libstddjb/selfpipe.html new file mode 100644 index 0000000..7eff430 --- /dev/null +++ b/doc/libstddjb/selfpipe.html @@ -0,0 +1,242 @@ + + + + + skalibs: the selfpipe library interface + + + + + + +

+libstddjb
+skalibs
+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. Most of libstddjb's wrappers +are there to protect system calls from EINTR. (And no, the SA_RESTART +option in sigaction() isn't protection +enough.) But signal handlers are +more than just pesky interruptions: they can totally change the +execution flow. 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. +
  3. 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.
  4. +
  5. In your event loop, add p[0] to the list of fds you're watching +for readability.
  6. +
+ +

+ 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. Of course, you still need to protect your +system calls against EINTR: the self-pipe trick doesn't prevent signals +from happening. +

+ +

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/untrapping 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 0 if +the operation succeeded, and -1 if it failed. If it succeeded, you +can forget about the trapped signal entirely.
+In our example, if r is 0, then a SIGTERM will instantly +trigger readability on fd. +

+ +
+int r = selfpipe_untrap(SIGTERM) ;
+
+ +

+Conversely, selfpipe_untrap() uncatches a signal; the selfpipe +will not manage it anymore. r is 0 if the operation succeeded +and -1 if it failed. +

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

+selfpipe_trap() and selfpipe_untrap() handle 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 0 if the operation succeeded and -1 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 their previous value. +

+ +

Any limitations ?

+ +

+ Some, as always. +

+ + + +

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. +

+ + + -- cgit v1.2.3