skalibs

Mirror/fork of https://skarnet.org/software/skalibs/
git clone https://ccx.te2000.cz/git/skalibs
Log | Files | Refs | README | LICENSE

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:
Mdoc/djblegacy.html | 2+-
Mdoc/libstddjb/safewrappers.html | 66+++++++++++++++++++++++++++++++++++-------------------------------
Mdoc/libstddjb/selfpipe.html | 23++++++++++++-----------
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&nbsp;? </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() ;