commit e4348f29c1e0bca44ea43b35cb18ca9b58dbe16b
parent 51e3113781aa42e38396d352ff77ea1f1bd29643
Author: Laurent Bercot <ska-skaware@skarnet.org>
Date: Mon, 8 Dec 2014 23:26:20 +0000
Documentation update: safe wrappers
Diffstat:
3 files changed, 48 insertions(+), 43 deletions(-)
diff --git a/doc/djblegacy.html b/doc/djblegacy.html
@@ -121,7 +121,7 @@ and network programmer's point of view, one thing is clear: <em>most
standard libc interfaces suck.</em> There is no buffered asynchronous
I/O. There is no timed I/O. There is no heap management helper. Even
<a href="libstddjb/safewrappers.html">simple system calls are not
-guaranteed to succeed!</a>
+always guaranteed to succeed!</a>
</p>
<p>
diff --git a/doc/libstddjb/safewrappers.html b/doc/libstddjb/safewrappers.html
@@ -40,51 +40,55 @@ handler, if any, has been executed).
</p>
<p>
- This means that the intended execution of the process is at the mercy
-of a stray signal. If a signal happens at the wrong time, a system call
-fails when it could have succeeded. This is not acceptable.
+ Most of the time, this is not an issue: unignored signals usually kill
+the process anyway. Or stop it - and when it resumes, system calls are
+simply restarted, not interrupted. EINTR can only happen when a signal
+handler has been installed, and a signal is actually caught by the
+signal handler during an interruptible system call. And to avoid EINTR,
+users can use the SA_RESTART flag when installing the signal handler with
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html">sigaction()</a>.
</p>
-<h2> The solution </h2>
+<p>
+ However, there is a common case where using SA_RESTART is a bad idea:
+when writing an asynchronous event loop.
+</p>
<p>
- So, in order to be perfectly reliable, when a program makes an interruptible
-system call, it <em>must</em> check whether the return value is -1 EINTR,
-and restart the system call if it is the case. This is annoying to write;
-so, <tt>libstddjb</tt> provides small wrappers around interruptible system
-calls, so that programmers can just call those <em>safe wrappers</em> and
-never bother with this again.
+ An asynchronous event loop is organized around a
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html">select()</a> or
+<a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html">poll()</a>
+system call that is the only blocking operation in the whole loop. That call takes a
+timeout as an argument. If a signal handler is installed with SA_RESTART and a
+signal arrives in the middle of the select/poll call - which happens often
+since it is blocking - then the select/poll is restarted with the same arguments,
+including the timeout. This is not good: time has elapsed, the timeout should be
+recomputed. Some versions of select update the values in the struct timeval even
+when select() is interrupted, but it is not portable, and not applicable to poll().
+So it is necessary, in this case, to have select/poll return -1 EINTR when a
+signal is caught. And that means SA_RESTART should not be used.
</p>
<p>
- The performance loss from having a wrapper layer is totally negligible
-compared to the cost of using a system call in the first place.
+ Which means that other system calls performed when the signal handler has
+been installed, for instance in the body of the loop, will <em>not</em> be
+protected, and can return -1 EINTR if a signal is caught at the wrong time.
</p>
-<h2> But isn't it what the SA_RESTART flag is meant to address? </h2>
+<h2> The solution </h2>
<p>
- Yes, it is. Unfortunately, SA_RESTART only protects interruptible
-system calls from signals you actually have control over, and set a
-handler for with
-<a href="http://www.opengroup.org/onlinepubs/9699919799/functions/sigaction.html">sigaction()</a>.
-This is not enough. You cannot decide that <em>every</em> signal sent
-to your process should have SA_RESTART behaviour; and the Single Unix
-specification says nothing about signals you do not control. For instance,
-you cannot trap SIGSTOP; SIGSTOP does not kill your process, which
-should resume flawlessly at the next SIGCONT; and according to the
-specification, it is valid for SIGSTOP and SIGCONT to <em>not</em>
-have SA_RESTART behaviour. So if you get a SIGSTOP while performing
-an interruptible system call, that system call may return -1 EINTR,
-this is not an OS bug, and there's nothing you can do about it with
-<tt>sigaction()</tt>.
+ So, in order to be perfectly reliable, when a program has caught a signal
+without SA_RESTART and makes an interruptible system call, it must check whether
+the return value is -1 EINTR, and restart the system call if it is the case.
+This is annoying to write; so, skalibs provides small wrappers around
+interruptible system calls, so that programmers can just call those safe wrappers
+and never bother with this again.
</p>
<p>
- SA_RESTART is only a partial solution: in other words, it doesn't work.
-Until the Single Unix specification explicitly states that untrapped
-non-lethal signals MUST have SA_RESTART behaviour by default, you
-<em>need</em> safe wrappers to protect interruptible system calls.
+ The performance loss from having a wrapper layer is totally negligible
+compared to the cost of using a system call in the first place.
</p>
</body>
diff --git a/doc/libstddjb/selfpipe.html b/doc/libstddjb/selfpipe.html
@@ -39,12 +39,9 @@ screw up, and write buggy code.
</p>
<p>
- 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() <a href="safewrappers.html">isn't protection
-enough</a>.) 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,
+ 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.
@@ -109,9 +106,13 @@ environment (the "bottom half" of the handler).
<p>
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 <em>still</em> need to protect your
-system calls against EINTR: the self-pipe trick doesn't prevent signals
-from happening.
+some peace of mind.
+</p>
+
+<p>
+ Note that in an asynchronous event loop, you need to protect your
+system calls against EINTR by using <a href="safewrappers.html">safe
+wrappers</a>.
</p>
<h2> How do I use it ? </h2>
@@ -167,14 +168,14 @@ r = selfpipe_trapset(&set) ;
<p>
<tt>selfpipe_trap()</tt> and <tt>selfpipe_untrap()</tt> handle signals one
by one. Alternatively (and often preferrably), you can use
-<tt>selfpipe_trapset()</tt> to directly handle signal sets. When you call
+<tt>selfpipe_trapset()</tt> to directly handle signal sets. When you call
<tt>selfpipe_trapset()</tt>, signals that are present in <tt>set</tt> will
be caught by the selfpipe, and signals that are absent from <tt>set</tt>
will be uncaught. <tt>r</tt> is 0 if the operation succeeded and -1 if it
failed.
</p>
-<h3> Handling events </h3>
+<h3> Handling events </h3>
<pre>
int c = selfpipe_read() ;